]> ncurses.scripts.mit.edu Git - ncurses.git/blob - test/ncurses.c
ncurses 5.7 - patch 20091031
[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.349 2009/10/24 21:28:36 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 #if HAVE_SLK_COLOR
2585         ,"F/B        -- cycle through foreground/background colors"
2586 #endif
2587         ,"ESC        -- return to main menu"
2588         ,""
2589         ,"Note: if activating the soft keys causes your terminal to scroll up"
2590         ,"one line, your terminal auto-scrolls when anything is written to the"
2591         ,"last screen position.  The ncurses code does not yet handle this"
2592         ,"gracefully."
2593     };
2594     unsigned j;
2595
2596     move(2, 0);
2597     for (j = 0; j < SIZEOF(table); ++j) {
2598         P(table[j]);
2599     }
2600     refresh();
2601 }
2602
2603 #if HAVE_SLK_COLOR
2604 static void
2605 call_slk_color(short fg, short bg)
2606 {
2607     init_pair(1, bg, fg);
2608     slk_color(1);
2609     mvprintw(SLK_WORK, 0, "Colors %d/%d\n", fg, bg);
2610     clrtoeol();
2611     refresh();
2612 }
2613 #endif
2614
2615 static void
2616 slk_test(void)
2617 /* exercise the soft keys */
2618 {
2619     int c, fmt = 1;
2620     char buf[9];
2621     char *s;
2622 #if HAVE_SLK_COLOR
2623     short fg = COLOR_BLACK;
2624     short bg = COLOR_WHITE;
2625 #endif
2626
2627     c = CTRL('l');
2628 #if HAVE_SLK_COLOR
2629     if (use_colors) {
2630         call_slk_color(fg, bg);
2631     }
2632 #endif
2633
2634     do {
2635         move(0, 0);
2636         switch (c) {
2637         case CTRL('l'):
2638             erase();
2639             attron(A_BOLD);
2640             mvaddstr(0, 20, "Soft Key Exerciser");
2641             attroff(A_BOLD);
2642
2643             slk_help();
2644             /* fall through */
2645
2646         case 'a':
2647             slk_restore();
2648             break;
2649
2650         case 'e':
2651             wclear(stdscr);
2652             break;
2653
2654         case 's':
2655             mvprintw(SLK_WORK, 0, "Press Q to stop the scrolling-test: ");
2656             while ((c = Getchar()) != 'Q' && (c != ERR))
2657                 addch((chtype) c);
2658             break;
2659
2660         case 'd':
2661             slk_clear();
2662             break;
2663
2664         case 'l':
2665             fmt = 0;
2666             break;
2667
2668         case 'c':
2669             fmt = 1;
2670             break;
2671
2672         case 'r':
2673             fmt = 2;
2674             break;
2675
2676         case '1':
2677         case '2':
2678         case '3':
2679         case '4':
2680         case '5':
2681         case '6':
2682         case '7':
2683         case '8':
2684             (void) mvaddstr(SLK_WORK, 0, "Please enter the label value: ");
2685             strcpy(buf, "");
2686             if ((s = slk_label(c - '0')) != 0) {
2687                 strncpy(buf, s, 8);
2688             }
2689             wGetstring(stdscr, buf, 8);
2690             slk_set((c - '0'), buf, fmt);
2691             slk_refresh();
2692             move(SLK_WORK, 0);
2693             clrtobot();
2694             break;
2695
2696         case case_QUIT:
2697             goto done;
2698
2699 #if HAVE_SLK_COLOR
2700         case 'F':
2701             if (use_colors) {
2702                 fg = (short) ((fg + 1) % COLORS);
2703                 call_slk_color(fg, bg);
2704             }
2705             break;
2706         case 'B':
2707             if (use_colors) {
2708                 bg = (short) ((bg + 1) % COLORS);
2709                 call_slk_color(fg, bg);
2710             }
2711             break;
2712 #endif
2713 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
2714         case KEY_RESIZE:
2715             wnoutrefresh(stdscr);
2716             break;
2717 #endif
2718
2719         default:
2720             beep();
2721         }
2722     } while (!isQuit(c = Getchar()));
2723
2724   done:
2725     slk_clear();
2726     erase();
2727     endwin();
2728 }
2729
2730 #if USE_WIDEC_SUPPORT
2731 #define SLKLEN 8
2732 static void
2733 wide_slk_test(void)
2734 /* exercise the soft keys */
2735 {
2736     int c, fmt = 1;
2737     wchar_t buf[SLKLEN + 1];
2738     char *s;
2739     short fg = COLOR_BLACK;
2740     short bg = COLOR_WHITE;
2741
2742     c = CTRL('l');
2743     if (use_colors) {
2744         call_slk_color(fg, bg);
2745     }
2746     do {
2747         move(0, 0);
2748         switch (c) {
2749         case CTRL('l'):
2750             erase();
2751             attr_on(WA_BOLD, NULL);
2752             mvaddstr(0, 20, "Soft Key Exerciser");
2753             attr_off(WA_BOLD, NULL);
2754
2755             slk_help();
2756             /* fall through */
2757
2758         case 'a':
2759             slk_restore();
2760             break;
2761
2762         case 'e':
2763             wclear(stdscr);
2764             break;
2765
2766         case 's':
2767             mvprintw(SLK_WORK, 0, "Press Q to stop the scrolling-test: ");
2768             while ((c = Getchar()) != 'Q' && (c != ERR))
2769                 addch((chtype) c);
2770             break;
2771
2772         case 'd':
2773             slk_clear();
2774             break;
2775
2776         case 'l':
2777             fmt = 0;
2778             break;
2779
2780         case 'c':
2781             fmt = 1;
2782             break;
2783
2784         case 'r':
2785             fmt = 2;
2786             break;
2787
2788         case '1':
2789         case '2':
2790         case '3':
2791         case '4':
2792         case '5':
2793         case '6':
2794         case '7':
2795         case '8':
2796             (void) mvaddstr(SLK_WORK, 0, "Please enter the label value: ");
2797             *buf = 0;
2798             if ((s = slk_label(c - '0')) != 0) {
2799                 char *temp = strdup(s);
2800                 size_t used = strlen(temp);
2801                 size_t want = SLKLEN;
2802                 size_t test;
2803 #ifndef state_unused
2804                 mbstate_t state;
2805 #endif
2806
2807                 buf[0] = L'\0';
2808                 while (want > 0 && used != 0) {
2809                     const char *base = s;
2810                     reset_mbytes(state);
2811                     test = count_mbytes(base, 0, &state);
2812                     if (test == (size_t) -1) {
2813                         temp[--used] = 0;
2814                     } else if (test > want) {
2815                         temp[--used] = 0;
2816                     } else {
2817                         reset_mbytes(state);
2818                         trans_mbytes(buf, base, want, &state);
2819                         break;
2820                     }
2821                 }
2822                 free(temp);
2823             }
2824             wGet_wstring(stdscr, buf, SLKLEN);
2825             slk_wset((c - '0'), buf, fmt);
2826             slk_refresh();
2827             move(SLK_WORK, 0);
2828             clrtobot();
2829             break;
2830
2831         case case_QUIT:
2832             goto done;
2833
2834         case 'F':
2835             if (use_colors) {
2836                 fg = (short) ((fg + 1) % COLORS);
2837                 call_slk_color(fg, bg);
2838             }
2839             break;
2840         case 'B':
2841             if (use_colors) {
2842                 bg = (short) ((bg + 1) % COLORS);
2843                 call_slk_color(fg, bg);
2844             }
2845             break;
2846 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
2847         case KEY_RESIZE:
2848             wnoutrefresh(stdscr);
2849             break;
2850 #endif
2851         default:
2852             beep();
2853         }
2854     } while (!isQuit(c = Getchar()));
2855
2856   done:
2857     slk_clear();
2858     erase();
2859     endwin();
2860 }
2861 #endif
2862 #endif /* SLK_INIT */
2863
2864 /****************************************************************************
2865  *
2866  * Alternate character-set stuff
2867  *
2868  ****************************************************************************/
2869 /* *INDENT-OFF* */
2870 static struct {
2871     chtype attr;
2872     const char *name;
2873 } attrs_to_cycle[] = {
2874     { A_NORMAL,         "normal" },
2875     { A_BOLD,           "bold" },
2876     { A_BLINK,          "blink" },
2877     { A_REVERSE,        "reverse" },
2878     { A_UNDERLINE,      "underline" },
2879 };
2880 /* *INDENT-ON* */
2881
2882 static bool
2883 cycle_attr(int ch, unsigned *at_code, chtype *attr)
2884 {
2885     bool result = TRUE;
2886
2887     switch (ch) {
2888     case 'v':
2889         if ((*at_code += 1) >= SIZEOF(attrs_to_cycle))
2890             *at_code = 0;
2891         break;
2892     case 'V':
2893         if (*at_code == 0)
2894             *at_code = SIZEOF(attrs_to_cycle) - 1;
2895         else
2896             *at_code -= 1;
2897         break;
2898     default:
2899         result = FALSE;
2900         break;
2901     }
2902     if (result)
2903         *attr = attrs_to_cycle[*at_code].attr;
2904     return result;
2905 }
2906
2907 static bool
2908 cycle_colors(int ch, int *fg, int *bg, short *pair)
2909 {
2910     bool result = FALSE;
2911
2912     if (use_colors) {
2913         result = TRUE;
2914         switch (ch) {
2915         case 'F':
2916             if ((*fg -= 1) < 0)
2917                 *fg = COLORS - 1;
2918             break;
2919         case 'f':
2920             if ((*fg += 1) >= COLORS)
2921                 *fg = 0;
2922             break;
2923         case 'B':
2924             if ((*bg -= 1) < 0)
2925                 *bg = COLORS - 1;
2926             break;
2927         case 'b':
2928             if ((*bg += 1) >= COLORS)
2929                 *bg = 0;
2930             break;
2931         default:
2932             result = FALSE;
2933             break;
2934         }
2935         if (result) {
2936             *pair = (short) (*fg != COLOR_BLACK || *bg != COLOR_BLACK);
2937             if (*pair != 0) {
2938                 *pair = 1;
2939                 if (init_pair(*pair, (short) *fg, (short) *bg) == ERR) {
2940                     result = FALSE;
2941                 }
2942             }
2943         }
2944     }
2945     return result;
2946 }
2947
2948 /* ISO 6429:  codes 0x80 to 0x9f may be control characters that cause the
2949  * terminal to perform functions.  The remaining codes can be graphic.
2950  */
2951 static void
2952 show_upper_chars(unsigned first, int repeat, attr_t attr, short pair)
2953 {
2954     bool C1 = (first == 128);
2955     unsigned code;
2956     unsigned last = first + 31;
2957     int reply;
2958
2959     erase();
2960     attron(A_BOLD);
2961     mvprintw(0, 20, "Display of %s Character Codes %d to %d",
2962              C1 ? "C1" : "GR", first, last);
2963     attroff(A_BOLD);
2964     refresh();
2965
2966     for (code = first; code <= last; code++) {
2967         int count = repeat;
2968         int row = 2 + ((int) (code - first) % 16);
2969         int col = ((int) (code - first) / 16) * COLS / 2;
2970         char tmp[80];
2971         sprintf(tmp, "%3u (0x%x)", code, code);
2972         mvprintw(row, col, "%*s: ", COLS / 4, tmp);
2973
2974         do {
2975             if (C1)
2976                 nodelay(stdscr, TRUE);
2977             echochar(colored_chtype(code, attr, pair));
2978             if (C1) {
2979                 /* (yes, this _is_ crude) */
2980                 while ((reply = Getchar()) != ERR) {
2981                     addch(UChar(reply));
2982                     napms(10);
2983                 }
2984                 nodelay(stdscr, FALSE);
2985             }
2986         } while (--count > 0);
2987     }
2988 }
2989
2990 #define PC_COLS 4
2991
2992 static void
2993 show_pc_chars(int repeat, attr_t attr, short pair)
2994 {
2995     unsigned code;
2996
2997     erase();
2998     attron(A_BOLD);
2999     mvprintw(0, 20, "Display of PC Character Codes");
3000     attroff(A_BOLD);
3001     refresh();
3002
3003     for (code = 0; code < 16; ++code) {
3004         mvprintw(2, (int) code * PC_COLS + 8, "%X", code);
3005     }
3006     for (code = 0; code < 256; code++) {
3007         int count = repeat;
3008         int row = 3 + (int) (code / 16) + (code >= 128);
3009         int col = 8 + (int) (code % 16) * PC_COLS;
3010         if ((code % 16) == 0)
3011             mvprintw(row, 0, "0x%02x:", code);
3012         move(row, col);
3013         do {
3014             switch (code) {
3015             case '\n':
3016             case '\r':
3017             case '\b':
3018             case '\f':
3019             case '\033':
3020             case 0x9b:
3021                 /*
3022                  * Skip the ones that do not work.
3023                  */
3024                 break;
3025             default:
3026                 addch(colored_chtype(code, A_ALTCHARSET | attr, pair));
3027                 break;
3028             }
3029         } while (--count > 0);
3030     }
3031 }
3032
3033 static void
3034 show_box_chars(int repeat, attr_t attr, short pair)
3035 {
3036     (void) repeat;
3037     attr |= COLOR_PAIR(pair);
3038
3039     erase();
3040     attron(A_BOLD);
3041     mvaddstr(0, 20, "Display of the ACS Line-Drawing Set");
3042     attroff(A_BOLD);
3043     refresh();
3044     /* *INDENT-OFF* */
3045     wborder(stdscr,
3046             colored_chtype(ACS_VLINE,    attr, pair),
3047             colored_chtype(ACS_VLINE,    attr, pair),
3048             colored_chtype(ACS_HLINE,    attr, pair),
3049             colored_chtype(ACS_HLINE,    attr, pair),
3050             colored_chtype(ACS_ULCORNER, attr, pair),
3051             colored_chtype(ACS_URCORNER, attr, pair),
3052             colored_chtype(ACS_LLCORNER, attr, pair),
3053             colored_chtype(ACS_LRCORNER, attr, pair));
3054     mvhline(LINES / 2, 0,        colored_chtype(ACS_HLINE, attr, pair), COLS);
3055     mvvline(0,         COLS / 2, colored_chtype(ACS_VLINE, attr, pair), LINES);
3056     mvaddch(0,         COLS / 2, colored_chtype(ACS_TTEE,  attr, pair));
3057     mvaddch(LINES / 2, COLS / 2, colored_chtype(ACS_PLUS,  attr, pair));
3058     mvaddch(LINES - 1, COLS / 2, colored_chtype(ACS_BTEE,  attr, pair));
3059     mvaddch(LINES / 2, 0,        colored_chtype(ACS_LTEE,  attr, pair));
3060     mvaddch(LINES / 2, COLS - 1, colored_chtype(ACS_RTEE,  attr, pair));
3061     /* *INDENT-ON* */
3062
3063 }
3064
3065 static int
3066 show_1_acs(int n, int repeat, const char *name, chtype code)
3067 {
3068     const int height = 16;
3069     int row = 2 + (n % height);
3070     int col = (n / height) * COLS / 2;
3071
3072     mvprintw(row, col, "%*s : ", COLS / 4, name);
3073     do {
3074         addch(code);
3075     } while (--repeat > 0);
3076     return n + 1;
3077 }
3078
3079 static void
3080 show_acs_chars(int repeat, attr_t attr, short pair)
3081 /* display the ACS character set */
3082 {
3083     int n;
3084
3085 #define BOTH(name) #name, colored_chtype(name, attr, pair)
3086
3087     erase();
3088     attron(A_BOLD);
3089     mvaddstr(0, 20, "Display of the ACS Character Set");
3090     attroff(A_BOLD);
3091     refresh();
3092
3093     n = show_1_acs(0, repeat, BOTH(ACS_ULCORNER));
3094     n = show_1_acs(n, repeat, BOTH(ACS_URCORNER));
3095     n = show_1_acs(n, repeat, BOTH(ACS_LLCORNER));
3096     n = show_1_acs(n, repeat, BOTH(ACS_LRCORNER));
3097
3098     n = show_1_acs(n, repeat, BOTH(ACS_LTEE));
3099     n = show_1_acs(n, repeat, BOTH(ACS_RTEE));
3100     n = show_1_acs(n, repeat, BOTH(ACS_TTEE));
3101     n = show_1_acs(n, repeat, BOTH(ACS_BTEE));
3102
3103     n = show_1_acs(n, repeat, BOTH(ACS_HLINE));
3104     n = show_1_acs(n, repeat, BOTH(ACS_VLINE));
3105
3106     /*
3107      * HPUX's ACS definitions are broken here.  Just give up.
3108      */
3109 #if !(defined(__hpux) && !defined(NCURSES_VERSION))
3110     n = show_1_acs(n, repeat, BOTH(ACS_LARROW));
3111     n = show_1_acs(n, repeat, BOTH(ACS_RARROW));
3112     n = show_1_acs(n, repeat, BOTH(ACS_UARROW));
3113     n = show_1_acs(n, repeat, BOTH(ACS_DARROW));
3114
3115     n = show_1_acs(n, repeat, BOTH(ACS_BLOCK));
3116     n = show_1_acs(n, repeat, BOTH(ACS_BOARD));
3117     n = show_1_acs(n, repeat, BOTH(ACS_LANTERN));
3118     n = show_1_acs(n, repeat, BOTH(ACS_BULLET));
3119     n = show_1_acs(n, repeat, BOTH(ACS_CKBOARD));
3120     n = show_1_acs(n, repeat, BOTH(ACS_DEGREE));
3121     n = show_1_acs(n, repeat, BOTH(ACS_DIAMOND));
3122     n = show_1_acs(n, repeat, BOTH(ACS_PLMINUS));
3123     n = show_1_acs(n, repeat, BOTH(ACS_PLUS));
3124
3125     n = show_1_acs(n, repeat, BOTH(ACS_GEQUAL));
3126     n = show_1_acs(n, repeat, BOTH(ACS_NEQUAL));
3127     n = show_1_acs(n, repeat, BOTH(ACS_LEQUAL));
3128
3129     n = show_1_acs(n, repeat, BOTH(ACS_STERLING));
3130     n = show_1_acs(n, repeat, BOTH(ACS_PI));
3131     n = show_1_acs(n, repeat, BOTH(ACS_S1));
3132     n = show_1_acs(n, repeat, BOTH(ACS_S3));
3133     n = show_1_acs(n, repeat, BOTH(ACS_S7));
3134     n = show_1_acs(n, repeat, BOTH(ACS_S9));
3135 #endif
3136 }
3137
3138 static void
3139 acs_display(void)
3140 {
3141     int c = 'a';
3142     char *term = getenv("TERM");
3143     const char *pch_kludge = ((term != 0 && strstr(term, "linux"))
3144                               ? "p=PC, "
3145                               : "");
3146     chtype attr = A_NORMAL;
3147     int digit = 0;
3148     int repeat = 1;
3149     int fg = COLOR_BLACK;
3150     int bg = COLOR_BLACK;
3151     unsigned at_code = 0;
3152     short pair = 0;
3153     void (*last_show_acs) (int, attr_t, short) = 0;
3154
3155     do {
3156         switch (c) {
3157         case CTRL('L'):
3158             Repaint();
3159             break;
3160         case 'a':
3161             ToggleAcs(last_show_acs, show_acs_chars);
3162             break;
3163         case 'p':
3164             if (*pch_kludge)
3165                 ToggleAcs(last_show_acs, show_pc_chars);
3166             else
3167                 beep();
3168             break;
3169         case 'x':
3170             ToggleAcs(last_show_acs, show_box_chars);
3171             break;
3172         case '0':
3173         case '1':
3174         case '2':
3175         case '3':
3176             digit = (c - '0');
3177             last_show_acs = 0;
3178             break;
3179         case '-':
3180             if (digit > 0) {
3181                 --digit;
3182                 last_show_acs = 0;
3183             } else {
3184                 beep();
3185             }
3186             break;
3187         case '+':
3188             if (digit < 3) {
3189                 ++digit;
3190                 last_show_acs = 0;
3191             } else {
3192                 beep();
3193             }
3194             break;
3195         case '>':
3196             if (repeat < (COLS / 4))
3197                 ++repeat;
3198             break;
3199         case '<':
3200             if (repeat > 1)
3201                 --repeat;
3202             break;
3203         default:
3204             if (cycle_attr(c, &at_code, &attr)
3205                 || cycle_colors(c, &fg, &bg, &pair)) {
3206                 break;
3207             } else {
3208                 beep();
3209             }
3210             break;
3211         }
3212         if (last_show_acs != 0)
3213             last_show_acs(repeat, attr, pair);
3214         else
3215             show_upper_chars((unsigned) (digit * 32 + 128), repeat, attr, pair);
3216
3217         mvprintw(LINES - 3, 0,
3218                  "Note: ANSI terminals may not display C1 characters.");
3219         mvprintw(LINES - 2, 0,
3220                  "Select: a=ACS, x=box, %s0=C1, 1-3,+/- non-ASCII, </> repeat, ESC=quit",
3221                  pch_kludge);
3222         if (use_colors) {
3223             mvprintw(LINES - 1, 0,
3224                      "v/V, f/F, b/B cycle through video attributes (%s) and color %d/%d.",
3225                      attrs_to_cycle[at_code].name,
3226                      fg, bg);
3227         } else {
3228             mvprintw(LINES - 1, 0,
3229                      "v/V cycles through video attributes (%s).",
3230                      attrs_to_cycle[at_code].name);
3231         }
3232         refresh();
3233     } while (!isQuit(c = Getchar()));
3234
3235     Pause();
3236     erase();
3237     endwin();
3238 }
3239
3240 #if USE_WIDEC_SUPPORT
3241 static cchar_t *
3242 merge_wide_attr(cchar_t *dst, const cchar_t *src, attr_t attr, short pair)
3243 {
3244     int count;
3245
3246     *dst = *src;
3247     do {
3248         TEST_CCHAR(src, count, {
3249             attr |= (test_attrs & A_ALTCHARSET);
3250             setcchar(dst, test_wch, attr, pair, NULL);
3251         }
3252         , {
3253             ;
3254         });
3255     } while (0);
3256     return dst;
3257 }
3258
3259 static void
3260 show_upper_widechars(int first, int repeat, int space, attr_t attr, short pair)
3261 {
3262     cchar_t temp;
3263     wchar_t code;
3264     int last = first + 31;
3265
3266     erase();
3267     attron(A_BOLD);
3268     mvprintw(0, 20, "Display of Character Codes %d to %d", first, last);
3269     attroff(A_BOLD);
3270
3271     for (code = first; (int) code <= last; code++) {
3272         int row = 2 + ((code - first) % 16);
3273         int col = ((code - first) / 16) * COLS / 2;
3274         wchar_t codes[10];
3275         char tmp[80];
3276         int count = repeat;
3277         int y, x;
3278
3279         memset(&codes, 0, sizeof(codes));
3280         codes[0] = code;
3281         sprintf(tmp, "%3ld (0x%lx)", (long) code, (long) code);
3282         mvprintw(row, col, "%*s: ", COLS / 4, tmp);
3283         setcchar(&temp, codes, attr, pair, 0);
3284         do {
3285             /*
3286              * Give non-spacing characters something to combine with.  If we
3287              * don't, they'll bunch up in a heap on the space after the ":".
3288              * Mark them with reverse-video to make them simpler to find on
3289              * the display.
3290              */
3291             if (wcwidth(code) == 0)
3292                 addch(space | (A_REVERSE ^ attr) | COLOR_PAIR(pair));
3293             /*
3294              * This could use add_wch(), but is done for comparison with the
3295              * normal 'f' test (and to make a test-case for echo_wchar()).
3296              * The screen will flicker because the erase() at the top of the
3297              * function is met by the builtin refresh() in echo_wchar().
3298              */
3299             echo_wchar(&temp);
3300             /*
3301              * The repeat-count may make text wrap - avoid that.
3302              */
3303             getyx(stdscr, y, x);
3304             if (x >= col + (COLS / 2) - 2)
3305                 break;
3306         } while (--count > 0);
3307     }
3308 }
3309
3310 static int
3311 show_1_wacs(int n, int repeat, const char *name, const cchar_t *code)
3312 {
3313     const int height = 16;
3314     int row = 2 + (n % height);
3315     int col = (n / height) * COLS / 2;
3316
3317     mvprintw(row, col, "%*s : ", COLS / 4, name);
3318     while (--repeat >= 0) {
3319         add_wch(code);
3320     }
3321     return n + 1;
3322 }
3323
3324 #define MERGE_ATTR(wch) merge_wide_attr(&temp, wch, attr, pair)
3325
3326 static void
3327 show_wacs_chars(int repeat, attr_t attr, short pair)
3328 /* display the wide-ACS character set */
3329 {
3330     cchar_t temp;
3331
3332     int n;
3333
3334 /*#define BOTH2(name) #name, &(name) */
3335 #define BOTH2(name) #name, MERGE_ATTR(name)
3336
3337     erase();
3338     attron(A_BOLD);
3339     mvaddstr(0, 20, "Display of the Wide-ACS Character Set");
3340     attroff(A_BOLD);
3341     refresh();
3342
3343     n = show_1_wacs(0, repeat, BOTH2(WACS_ULCORNER));
3344     n = show_1_wacs(n, repeat, BOTH2(WACS_URCORNER));
3345     n = show_1_wacs(n, repeat, BOTH2(WACS_LLCORNER));
3346     n = show_1_wacs(n, repeat, BOTH2(WACS_LRCORNER));
3347
3348     n = show_1_wacs(n, repeat, BOTH2(WACS_LTEE));
3349     n = show_1_wacs(n, repeat, BOTH2(WACS_RTEE));
3350     n = show_1_wacs(n, repeat, BOTH2(WACS_TTEE));
3351     n = show_1_wacs(n, repeat, BOTH2(WACS_BTEE));
3352
3353     n = show_1_wacs(n, repeat, BOTH2(WACS_HLINE));
3354     n = show_1_wacs(n, repeat, BOTH2(WACS_VLINE));
3355
3356     n = show_1_wacs(n, repeat, BOTH2(WACS_LARROW));
3357     n = show_1_wacs(n, repeat, BOTH2(WACS_RARROW));
3358     n = show_1_wacs(n, repeat, BOTH2(WACS_UARROW));
3359     n = show_1_wacs(n, repeat, BOTH2(WACS_DARROW));
3360
3361     n = show_1_wacs(n, repeat, BOTH2(WACS_BLOCK));
3362     n = show_1_wacs(n, repeat, BOTH2(WACS_BOARD));
3363     n = show_1_wacs(n, repeat, BOTH2(WACS_LANTERN));
3364     n = show_1_wacs(n, repeat, BOTH2(WACS_BULLET));
3365     n = show_1_wacs(n, repeat, BOTH2(WACS_CKBOARD));
3366     n = show_1_wacs(n, repeat, BOTH2(WACS_DEGREE));
3367     n = show_1_wacs(n, repeat, BOTH2(WACS_DIAMOND));
3368     n = show_1_wacs(n, repeat, BOTH2(WACS_PLMINUS));
3369     n = show_1_wacs(n, repeat, BOTH2(WACS_PLUS));
3370
3371 #ifdef CURSES_WACS_ARRAY
3372     n = show_1_wacs(n, repeat, BOTH2(WACS_GEQUAL));
3373     n = show_1_wacs(n, repeat, BOTH2(WACS_NEQUAL));
3374     n = show_1_wacs(n, repeat, BOTH2(WACS_LEQUAL));
3375
3376     n = show_1_wacs(n, repeat, BOTH2(WACS_STERLING));
3377     n = show_1_wacs(n, repeat, BOTH2(WACS_PI));
3378     n = show_1_wacs(n, repeat, BOTH2(WACS_S1));
3379     n = show_1_wacs(n, repeat, BOTH2(WACS_S3));
3380     n = show_1_wacs(n, repeat, BOTH2(WACS_S7));
3381     n = show_1_wacs(n, repeat, BOTH2(WACS_S9));
3382 #endif
3383 }
3384
3385 #ifdef WACS_D_PLUS
3386 static void
3387 show_wacs_chars_double(int repeat, attr_t attr, short pair)
3388 /* display the wide-ACS character set */
3389 {
3390     cchar_t temp;
3391
3392     int n;
3393
3394 /*#define BOTH2(name) #name, &(name) */
3395 #define BOTH2(name) #name, MERGE_ATTR(name)
3396
3397     erase();
3398     attron(A_BOLD);
3399     mvaddstr(0, 20, "Display of the Wide-ACS Character Set");
3400     attroff(A_BOLD);
3401     refresh();
3402
3403     n = show_1_wacs(0, repeat, BOTH2(WACS_D_ULCORNER));
3404     n = show_1_wacs(n, repeat, BOTH2(WACS_D_URCORNER));
3405     n = show_1_wacs(n, repeat, BOTH2(WACS_D_LLCORNER));
3406     n = show_1_wacs(n, repeat, BOTH2(WACS_D_LRCORNER));
3407
3408     n = show_1_wacs(n, repeat, BOTH2(WACS_D_LTEE));
3409     n = show_1_wacs(n, repeat, BOTH2(WACS_D_RTEE));
3410     n = show_1_wacs(n, repeat, BOTH2(WACS_D_TTEE));
3411     n = show_1_wacs(n, repeat, BOTH2(WACS_D_BTEE));
3412
3413     n = show_1_wacs(n, repeat, BOTH2(WACS_D_HLINE));
3414     n = show_1_wacs(n, repeat, BOTH2(WACS_D_VLINE));
3415
3416     n = show_1_wacs(n, repeat, BOTH2(WACS_LARROW));
3417     n = show_1_wacs(n, repeat, BOTH2(WACS_RARROW));
3418     n = show_1_wacs(n, repeat, BOTH2(WACS_UARROW));
3419     n = show_1_wacs(n, repeat, BOTH2(WACS_DARROW));
3420
3421     n = show_1_wacs(n, repeat, BOTH2(WACS_BLOCK));
3422     n = show_1_wacs(n, repeat, BOTH2(WACS_BOARD));
3423     n = show_1_wacs(n, repeat, BOTH2(WACS_LANTERN));
3424     n = show_1_wacs(n, repeat, BOTH2(WACS_BULLET));
3425     n = show_1_wacs(n, repeat, BOTH2(WACS_CKBOARD));
3426     n = show_1_wacs(n, repeat, BOTH2(WACS_DEGREE));
3427     n = show_1_wacs(n, repeat, BOTH2(WACS_DIAMOND));
3428     n = show_1_wacs(n, repeat, BOTH2(WACS_PLMINUS));
3429     n = show_1_wacs(n, repeat, BOTH2(WACS_PLUS));
3430
3431 #ifdef CURSES_WACS_ARRAY
3432     n = show_1_wacs(n, repeat, BOTH2(WACS_GEQUAL));
3433     n = show_1_wacs(n, repeat, BOTH2(WACS_NEQUAL));
3434     n = show_1_wacs(n, repeat, BOTH2(WACS_LEQUAL));
3435
3436     n = show_1_wacs(n, repeat, BOTH2(WACS_STERLING));
3437     n = show_1_wacs(n, repeat, BOTH2(WACS_PI));
3438     n = show_1_wacs(n, repeat, BOTH2(WACS_S1));
3439     n = show_1_wacs(n, repeat, BOTH2(WACS_S3));
3440     n = show_1_wacs(n, repeat, BOTH2(WACS_S7));
3441     n = show_1_wacs(n, repeat, BOTH2(WACS_S9));
3442 #endif
3443 }
3444 #endif
3445
3446 #ifdef WACS_T_PLUS
3447 static void
3448 show_wacs_chars_thick(int repeat, attr_t attr, short pair)
3449 /* display the wide-ACS character set */
3450 {
3451     cchar_t temp;
3452
3453     int n;
3454
3455 /*#define BOTH2(name) #name, &(name) */
3456 #define BOTH2(name) #name, MERGE_ATTR(name)
3457
3458     erase();
3459     attron(A_BOLD);
3460     mvaddstr(0, 20, "Display of the Wide-ACS Character Set");
3461     attroff(A_BOLD);
3462     refresh();
3463
3464     n = show_1_wacs(0, repeat, BOTH2(WACS_T_ULCORNER));
3465     n = show_1_wacs(n, repeat, BOTH2(WACS_T_URCORNER));
3466     n = show_1_wacs(n, repeat, BOTH2(WACS_T_LLCORNER));
3467     n = show_1_wacs(n, repeat, BOTH2(WACS_T_LRCORNER));
3468
3469     n = show_1_wacs(n, repeat, BOTH2(WACS_T_LTEE));
3470     n = show_1_wacs(n, repeat, BOTH2(WACS_T_RTEE));
3471     n = show_1_wacs(n, repeat, BOTH2(WACS_T_TTEE));
3472     n = show_1_wacs(n, repeat, BOTH2(WACS_T_BTEE));
3473
3474     n = show_1_wacs(n, repeat, BOTH2(WACS_T_HLINE));
3475     n = show_1_wacs(n, repeat, BOTH2(WACS_T_VLINE));
3476
3477     n = show_1_wacs(n, repeat, BOTH2(WACS_LARROW));
3478     n = show_1_wacs(n, repeat, BOTH2(WACS_RARROW));
3479     n = show_1_wacs(n, repeat, BOTH2(WACS_UARROW));
3480     n = show_1_wacs(n, repeat, BOTH2(WACS_DARROW));
3481
3482     n = show_1_wacs(n, repeat, BOTH2(WACS_BLOCK));
3483     n = show_1_wacs(n, repeat, BOTH2(WACS_BOARD));
3484     n = show_1_wacs(n, repeat, BOTH2(WACS_LANTERN));
3485     n = show_1_wacs(n, repeat, BOTH2(WACS_BULLET));
3486     n = show_1_wacs(n, repeat, BOTH2(WACS_CKBOARD));
3487     n = show_1_wacs(n, repeat, BOTH2(WACS_DEGREE));
3488     n = show_1_wacs(n, repeat, BOTH2(WACS_DIAMOND));
3489     n = show_1_wacs(n, repeat, BOTH2(WACS_PLMINUS));
3490     n = show_1_wacs(n, repeat, BOTH2(WACS_PLUS));
3491
3492 #ifdef CURSES_WACS_ARRAY
3493     n = show_1_wacs(n, repeat, BOTH2(WACS_GEQUAL));
3494     n = show_1_wacs(n, repeat, BOTH2(WACS_NEQUAL));
3495     n = show_1_wacs(n, repeat, BOTH2(WACS_LEQUAL));
3496
3497     n = show_1_wacs(n, repeat, BOTH2(WACS_STERLING));
3498     n = show_1_wacs(n, repeat, BOTH2(WACS_PI));
3499     n = show_1_wacs(n, repeat, BOTH2(WACS_S1));
3500     n = show_1_wacs(n, repeat, BOTH2(WACS_S3));
3501     n = show_1_wacs(n, repeat, BOTH2(WACS_S7));
3502     n = show_1_wacs(n, repeat, BOTH2(WACS_S9));
3503 #endif
3504 }
3505 #endif
3506
3507 #undef MERGE_ATTR
3508
3509 #define MERGE_ATTR(n,wch) merge_wide_attr(&temp[n], wch, attr, pair)
3510
3511 static void
3512 show_wbox_chars(int repeat, attr_t attr, short pair)
3513 {
3514     cchar_t temp[8];
3515
3516     (void) repeat;
3517     erase();
3518     attron(A_BOLD);
3519     mvaddstr(0, 20, "Display of the Wide-ACS Line-Drawing Set");
3520     attroff(A_BOLD);
3521     refresh();
3522
3523     wborder_set(stdscr,
3524                 MERGE_ATTR(0, WACS_VLINE),
3525                 MERGE_ATTR(1, WACS_VLINE),
3526                 MERGE_ATTR(2, WACS_HLINE),
3527                 MERGE_ATTR(3, WACS_HLINE),
3528                 MERGE_ATTR(4, WACS_ULCORNER),
3529                 MERGE_ATTR(5, WACS_URCORNER),
3530                 MERGE_ATTR(6, WACS_LLCORNER),
3531                 MERGE_ATTR(7, WACS_LRCORNER));
3532     /* *INDENT-OFF* */
3533     mvhline_set(LINES / 2, 0,        MERGE_ATTR(0, WACS_HLINE), COLS);
3534     mvvline_set(0,         COLS / 2, MERGE_ATTR(0, WACS_VLINE), LINES);
3535     mvadd_wch(0,           COLS / 2, MERGE_ATTR(0, WACS_TTEE));
3536     mvadd_wch(LINES / 2,   COLS / 2, MERGE_ATTR(0, WACS_PLUS));
3537     mvadd_wch(LINES - 1,   COLS / 2, MERGE_ATTR(0, WACS_BTEE));
3538     mvadd_wch(LINES / 2,   0,        MERGE_ATTR(0, WACS_LTEE));
3539     mvadd_wch(LINES / 2,   COLS - 1, MERGE_ATTR(0, WACS_RTEE));
3540     /* *INDENT-ON* */
3541
3542 }
3543
3544 #undef MERGE_ATTR
3545
3546 static int
3547 show_2_wacs(int n, const char *name, const char *code, attr_t attr, short pair)
3548 {
3549     const int height = 16;
3550     int row = 2 + (n % height);
3551     int col = (n / height) * COLS / 2;
3552     char temp[80];
3553
3554     mvprintw(row, col, "%*s : ", COLS / 4, name);
3555     attr_set(attr, pair, 0);
3556     addstr(strcpy(temp, code));
3557     attr_set(A_NORMAL, 0, 0);
3558     return n + 1;
3559 }
3560
3561 #define SHOW_UTF8(n, name, code) show_2_wacs(n, name, code, attr, pair)
3562
3563 static void
3564 show_utf8_chars(int repeat, attr_t attr, short pair)
3565 {
3566     int n;
3567
3568     (void) repeat;
3569     erase();
3570     attron(A_BOLD);
3571     mvaddstr(0, 20, "Display of the Wide-ACS Character Set");
3572     attroff(A_BOLD);
3573     refresh();
3574     /* *INDENT-OFF* */
3575     n = SHOW_UTF8(0, "WACS_ULCORNER",   "\342\224\214");
3576     n = SHOW_UTF8(n, "WACS_URCORNER",   "\342\224\220");
3577     n = SHOW_UTF8(n, "WACS_LLCORNER",   "\342\224\224");
3578     n = SHOW_UTF8(n, "WACS_LRCORNER",   "\342\224\230");
3579
3580     n = SHOW_UTF8(n, "WACS_LTEE",       "\342\224\234");
3581     n = SHOW_UTF8(n, "WACS_RTEE",       "\342\224\244");
3582     n = SHOW_UTF8(n, "WACS_TTEE",       "\342\224\254");
3583     n = SHOW_UTF8(n, "WACS_BTEE",       "\342\224\264");
3584
3585     n = SHOW_UTF8(n, "WACS_HLINE",      "\342\224\200");
3586     n = SHOW_UTF8(n, "WACS_VLINE",      "\342\224\202");
3587
3588     n = SHOW_UTF8(n, "WACS_LARROW",     "\342\206\220");
3589     n = SHOW_UTF8(n, "WACS_RARROW",     "\342\206\222");
3590     n = SHOW_UTF8(n, "WACS_UARROW",     "\342\206\221");
3591     n = SHOW_UTF8(n, "WACS_DARROW",     "\342\206\223");
3592
3593     n = SHOW_UTF8(n, "WACS_BLOCK",      "\342\226\256");
3594     n = SHOW_UTF8(n, "WACS_BOARD",      "\342\226\222");
3595     n = SHOW_UTF8(n, "WACS_LANTERN",    "\342\230\203");
3596     n = SHOW_UTF8(n, "WACS_BULLET",     "\302\267");
3597     n = SHOW_UTF8(n, "WACS_CKBOARD",    "\342\226\222");
3598     n = SHOW_UTF8(n, "WACS_DEGREE",     "\302\260");
3599     n = SHOW_UTF8(n, "WACS_DIAMOND",    "\342\227\206");
3600     n = SHOW_UTF8(n, "WACS_PLMINUS",    "\302\261");
3601     n = SHOW_UTF8(n, "WACS_PLUS",       "\342\224\274");
3602     n = SHOW_UTF8(n, "WACS_GEQUAL",     "\342\211\245");
3603     n = SHOW_UTF8(n, "WACS_NEQUAL",     "\342\211\240");
3604     n = SHOW_UTF8(n, "WACS_LEQUAL",     "\342\211\244");
3605
3606     n = SHOW_UTF8(n, "WACS_STERLING",   "\302\243");
3607     n = SHOW_UTF8(n, "WACS_PI",         "\317\200");
3608     n = SHOW_UTF8(n, "WACS_S1",         "\342\216\272");
3609     n = SHOW_UTF8(n, "WACS_S3",         "\342\216\273");
3610     n = SHOW_UTF8(n, "WACS_S7",         "\342\216\274");
3611     n = SHOW_UTF8(n, "WACS_S9",         "\342\216\275");
3612     /* *INDENT-ON* */
3613
3614 }
3615
3616 /* display the wide-ACS character set */
3617 static void
3618 wide_acs_display(void)
3619 {
3620     int c = 'a';
3621     int digit = 0;
3622     int repeat = 1;
3623     int space = ' ';
3624     chtype attr = A_NORMAL;
3625     int fg = COLOR_BLACK;
3626     int bg = COLOR_BLACK;
3627     unsigned at_code = 0;
3628     short pair = 0;
3629     void (*last_show_wacs) (int, attr_t, short) = 0;
3630
3631     do {
3632         switch (c) {
3633         case CTRL('L'):
3634             Repaint();
3635             break;
3636         case 'a':
3637             ToggleAcs(last_show_wacs, show_wacs_chars);
3638             break;
3639 #ifdef WACS_D_PLUS
3640         case 'd':
3641             ToggleAcs(last_show_wacs, show_wacs_chars_double);
3642             break;
3643 #endif
3644 #ifdef WACS_T_PLUS
3645         case 't':
3646             ToggleAcs(last_show_wacs, show_wacs_chars_thick);
3647             break;
3648 #endif
3649         case 'x':
3650             ToggleAcs(last_show_wacs, show_wbox_chars);
3651             break;
3652         case 'u':
3653             ToggleAcs(last_show_wacs, show_utf8_chars);
3654             break;
3655         default:
3656             if (c < 256 && isdigit(c)) {
3657                 digit = (c - '0');
3658                 last_show_wacs = 0;
3659             } else if (c == '+') {
3660                 ++digit;
3661                 last_show_wacs = 0;
3662             } else if (c == '-' && digit > 0) {
3663                 --digit;
3664                 last_show_wacs = 0;
3665             } else if (c == '>' && repeat < (COLS / 4)) {
3666                 ++repeat;
3667             } else if (c == '<' && repeat > 1) {
3668                 --repeat;
3669             } else if (c == '_') {
3670                 space = (space == ' ') ? '_' : ' ';
3671                 last_show_wacs = 0;
3672             } else if (cycle_attr(c, &at_code, &attr)
3673                        || cycle_colors(c, &fg, &bg, &pair)) {
3674                 if (last_show_wacs != 0)
3675                     break;
3676             } else {
3677                 beep();
3678                 break;
3679             }
3680             break;
3681         }
3682         if (last_show_wacs != 0)
3683             last_show_wacs(repeat, attr, pair);
3684         else
3685             show_upper_widechars(digit * 32 + 128, repeat, space, attr, pair);
3686
3687         mvprintw(LINES - 3, 0,
3688                  "Select: a/d/t WACS, x box, u UTF-8, 0-9,+/- non-ASCII, </> repeat, ESC=quit");
3689         if (use_colors) {
3690             mvprintw(LINES - 2, 0,
3691                      "v/V, f/F, b/B cycle through video attributes (%s) and color %d/%d.",
3692                      attrs_to_cycle[at_code].name,
3693                      fg, bg);
3694         } else {
3695             mvprintw(LINES - 2, 0,
3696                      "v/V cycles through video attributes (%s).",
3697                      attrs_to_cycle[at_code].name);
3698         }
3699         refresh();
3700     } while (!isQuit(c = Getchar()));
3701
3702     Pause();
3703     erase();
3704     endwin();
3705 }
3706
3707 #endif
3708
3709 /*
3710  * Graphic-rendition test (adapted from vttest)
3711  */
3712 static void
3713 test_sgr_attributes(void)
3714 {
3715     int pass;
3716
3717     for (pass = 0; pass < 2; pass++) {
3718         chtype normal = ((pass == 0 ? A_NORMAL : A_REVERSE)) | BLANK;
3719
3720         /* Use non-default colors if possible to exercise bce a little */
3721         if (use_colors) {
3722             init_pair(1, COLOR_WHITE, COLOR_BLUE);
3723             normal |= COLOR_PAIR(1);
3724         }
3725         bkgdset(normal);
3726         erase();
3727         mvprintw(1, 20, "Graphic rendition test pattern:");
3728
3729         mvprintw(4, 1, "vanilla");
3730
3731 #define set_sgr(mask) bkgdset((normal^(mask)));
3732         set_sgr(A_BOLD);
3733         mvprintw(4, 40, "bold");
3734
3735         set_sgr(A_UNDERLINE);
3736         mvprintw(6, 6, "underline");
3737
3738         set_sgr(A_BOLD | A_UNDERLINE);
3739         mvprintw(6, 45, "bold underline");
3740
3741         set_sgr(A_BLINK);
3742         mvprintw(8, 1, "blink");
3743
3744         set_sgr(A_BLINK | A_BOLD);
3745         mvprintw(8, 40, "bold blink");
3746
3747         set_sgr(A_UNDERLINE | A_BLINK);
3748         mvprintw(10, 6, "underline blink");
3749
3750         set_sgr(A_BOLD | A_UNDERLINE | A_BLINK);
3751         mvprintw(10, 45, "bold underline blink");
3752
3753         set_sgr(A_REVERSE);
3754         mvprintw(12, 1, "negative");
3755
3756         set_sgr(A_BOLD | A_REVERSE);
3757         mvprintw(12, 40, "bold negative");
3758
3759         set_sgr(A_UNDERLINE | A_REVERSE);
3760         mvprintw(14, 6, "underline negative");
3761
3762         set_sgr(A_BOLD | A_UNDERLINE | A_REVERSE);
3763         mvprintw(14, 45, "bold underline negative");
3764
3765         set_sgr(A_BLINK | A_REVERSE);
3766         mvprintw(16, 1, "blink negative");
3767
3768         set_sgr(A_BOLD | A_BLINK | A_REVERSE);
3769         mvprintw(16, 40, "bold blink negative");
3770
3771         set_sgr(A_UNDERLINE | A_BLINK | A_REVERSE);
3772         mvprintw(18, 6, "underline blink negative");
3773
3774         set_sgr(A_BOLD | A_UNDERLINE | A_BLINK | A_REVERSE);
3775         mvprintw(18, 45, "bold underline blink negative");
3776
3777         bkgdset(normal);
3778         mvprintw(LINES - 2, 1, "%s background. ", pass == 0 ? "Dark" :
3779                  "Light");
3780         clrtoeol();
3781         Pause();
3782     }
3783
3784     bkgdset(A_NORMAL | BLANK);
3785     erase();
3786     endwin();
3787 }
3788
3789 /****************************************************************************
3790  *
3791  * Windows and scrolling tester.
3792  *
3793  ****************************************************************************/
3794
3795 #define BOTLINES        4       /* number of line stolen from screen bottom */
3796
3797 typedef struct {
3798     int y, x;
3799 } pair;
3800
3801 #define FRAME struct frame
3802 FRAME
3803 {
3804     FRAME *next, *last;
3805     bool do_scroll;
3806     bool do_keypad;
3807     WINDOW *wind;
3808 };
3809
3810 #if defined(NCURSES_VERSION)
3811 #if (NCURSES_VERSION_PATCH < 20070331) && NCURSES_EXT_FUNCS
3812 #define is_keypad(win)   (win)->_use_keypad
3813 #define is_scrollok(win) (win)->_scroll
3814 #elif !defined(is_keypad)
3815 #define is_keypad(win)   FALSE
3816 #define is_scrollok(win) FALSE
3817 #endif
3818 #else
3819 #define is_keypad(win)   FALSE
3820 #define is_scrollok(win) FALSE
3821 #endif
3822
3823 static WINDOW *
3824 frame_win(FRAME * curp)
3825 {
3826     return (curp != 0) ? curp->wind : stdscr;
3827 }
3828
3829 /* We need to know if these flags are actually set, so don't look in FRAME.
3830  * These names are known to work with SVr4 curses as well as ncurses.  The
3831  * _use_keypad name does not work with Solaris 8.
3832  */
3833 static bool
3834 HaveKeypad(FRAME * curp)
3835 {
3836     WINDOW *win = frame_win(curp);
3837     (void) win;
3838     return is_keypad(win);
3839 }
3840
3841 static bool
3842 HaveScroll(FRAME * curp)
3843 {
3844     WINDOW *win = frame_win(curp);
3845     (void) win;
3846     return is_scrollok(win);
3847 }
3848
3849 static void
3850 newwin_legend(FRAME * curp)
3851 {
3852     static const struct {
3853         const char *msg;
3854         int code;
3855     } legend[] = {
3856         {
3857             "^C = create window", 0
3858         },
3859         {
3860             "^N = next window", 0
3861         },
3862         {
3863             "^P = previous window", 0
3864         },
3865         {
3866             "^F = scroll forward", 0
3867         },
3868         {
3869             "^B = scroll backward", 0
3870         },
3871         {
3872             "^K = keypad(%s)", 1
3873         },
3874         {
3875             "^S = scrollok(%s)", 2
3876         },
3877         {
3878             "^W = save window to file", 0
3879         },
3880         {
3881             "^R = restore window", 0
3882         },
3883 #if HAVE_WRESIZE
3884         {
3885             "^X = resize", 0
3886         },
3887 #endif
3888         {
3889             "^Q%s = exit", 3
3890         }
3891     };
3892     size_t n;
3893     int x;
3894     bool do_keypad = HaveKeypad(curp);
3895     bool do_scroll = HaveScroll(curp);
3896     char buf[BUFSIZ];
3897
3898     move(LINES - 4, 0);
3899     for (n = 0; n < SIZEOF(legend); n++) {
3900         switch (legend[n].code) {
3901         default:
3902             strcpy(buf, legend[n].msg);
3903             break;
3904         case 1:
3905             sprintf(buf, legend[n].msg, do_keypad ? "yes" : "no");
3906             break;
3907         case 2:
3908             sprintf(buf, legend[n].msg, do_scroll ? "yes" : "no");
3909             break;
3910         case 3:
3911             sprintf(buf, legend[n].msg, do_keypad ? "/ESC" : "");
3912             break;
3913         }
3914         x = getcurx(stdscr);
3915         addstr((COLS < (x + 3 + (int) strlen(buf))) ? "\n" : (n ? ", " : ""));
3916         addstr(buf);
3917     }
3918     clrtoeol();
3919 }
3920
3921 static void
3922 transient(FRAME * curp, NCURSES_CONST char *msg)
3923 {
3924     newwin_legend(curp);
3925     if (msg) {
3926         mvaddstr(LINES - 1, 0, msg);
3927         refresh();
3928         napms(1000);
3929     }
3930
3931     move(LINES - 1, 0);
3932     printw("%s characters are echoed, window should %sscroll.",
3933            HaveKeypad(curp) ? "Non-arrow" : "All other",
3934            HaveScroll(curp) ? "" : "not ");
3935     clrtoeol();
3936 }
3937
3938 static void
3939 newwin_report(FRAME * curp)
3940 /* report on the cursor's current position, then restore it */
3941 {
3942     WINDOW *win = frame_win(curp);
3943     int y, x;
3944
3945     if (win != stdscr)
3946         transient(curp, (char *) 0);
3947     getyx(win, y, x);
3948     move(LINES - 1, COLS - 17);
3949     printw("Y = %2d X = %2d", y, x);
3950     if (win != stdscr)
3951         refresh();
3952     else
3953         wmove(win, y, x);
3954 }
3955
3956 static pair *
3957 selectcell(int uli, int ulj, int lri, int lrj)
3958 /* arrows keys move cursor, return location at current on non-arrow key */
3959 {
3960     static pair res;            /* result cell */
3961     int si = lri - uli + 1;     /* depth of the select area */
3962     int sj = lrj - ulj + 1;     /* width of the select area */
3963     int i = 0, j = 0;           /* offsets into the select area */
3964
3965     res.y = uli;
3966     res.x = ulj;
3967     for (;;) {
3968         move(uli + i, ulj + j);
3969         newwin_report((FRAME *) 0);
3970
3971         switch (Getchar()) {
3972         case KEY_UP:
3973             i += si - 1;
3974             break;
3975         case KEY_DOWN:
3976             i++;
3977             break;
3978         case KEY_LEFT:
3979             j += sj - 1;
3980             break;
3981         case KEY_RIGHT:
3982             j++;
3983             break;
3984         case case_QUIT:
3985             return ((pair *) 0);
3986 #ifdef NCURSES_MOUSE_VERSION
3987         case KEY_MOUSE:
3988             {
3989                 MEVENT event;
3990
3991                 getmouse(&event);
3992                 if (event.y > uli && event.x > ulj) {
3993                     i = event.y - uli;
3994                     j = event.x - ulj;
3995                 } else {
3996                     beep();
3997                     break;
3998                 }
3999             }
4000             /* FALLTHRU */
4001 #endif
4002         default:
4003             res.y = uli + i;
4004             res.x = ulj + j;
4005             return (&res);
4006         }
4007         i %= si;
4008         j %= sj;
4009     }
4010 }
4011
4012 static void
4013 outerbox(pair ul, pair lr, bool onoff)
4014 /* draw or erase a box *outside* the given pair of corners */
4015 {
4016     mvaddch(ul.y - 1, lr.x - 1, onoff ? ACS_ULCORNER : ' ');
4017     mvaddch(ul.y - 1, lr.x + 1, onoff ? ACS_URCORNER : ' ');
4018     mvaddch(lr.y + 1, lr.x + 1, onoff ? ACS_LRCORNER : ' ');
4019     mvaddch(lr.y + 1, ul.x - 1, onoff ? ACS_LLCORNER : ' ');
4020     move(ul.y - 1, ul.x);
4021     hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
4022     move(ul.y, ul.x - 1);
4023     vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
4024     move(lr.y + 1, ul.x);
4025     hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
4026     move(ul.y, lr.x + 1);
4027     vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
4028 }
4029
4030 static WINDOW *
4031 getwindow(void)
4032 /* Ask user for a window definition */
4033 {
4034     WINDOW *rwindow;
4035     pair ul, lr, *tmp;
4036
4037     move(0, 0);
4038     clrtoeol();
4039     addstr("Use arrows to move cursor, anything else to mark corner 1");
4040     refresh();
4041     if ((tmp = selectcell(2, 1, LINES - BOTLINES - 2, COLS - 2)) == (pair *) 0)
4042         return ((WINDOW *) 0);
4043     memcpy(&ul, tmp, sizeof(pair));
4044     mvaddch(ul.y - 1, ul.x - 1, ACS_ULCORNER);
4045     move(0, 0);
4046     clrtoeol();
4047     addstr("Use arrows to move cursor, anything else to mark corner 2");
4048     refresh();
4049     if ((tmp = selectcell(ul.y, ul.x, LINES - BOTLINES - 2, COLS - 2)) ==
4050         (pair *) 0)
4051         return ((WINDOW *) 0);
4052     memcpy(&lr, tmp, sizeof(pair));
4053
4054     rwindow = subwin(stdscr, lr.y - ul.y + 1, lr.x - ul.x + 1, ul.y, ul.x);
4055
4056     outerbox(ul, lr, TRUE);
4057     refresh();
4058
4059     wrefresh(rwindow);
4060
4061     move(0, 0);
4062     clrtoeol();
4063     return (rwindow);
4064 }
4065
4066 static void
4067 newwin_move(FRAME * curp, int dy, int dx)
4068 {
4069     WINDOW *win = frame_win(curp);
4070     int cur_y, cur_x;
4071     int max_y, max_x;
4072
4073     getyx(win, cur_y, cur_x);
4074     getmaxyx(win, max_y, max_x);
4075     if ((cur_x += dx) < 0)
4076         cur_x = 0;
4077     else if (cur_x >= max_x)
4078         cur_x = max_x - 1;
4079     if ((cur_y += dy) < 0)
4080         cur_y = 0;
4081     else if (cur_y >= max_y)
4082         cur_y = max_y - 1;
4083     wmove(win, cur_y, cur_x);
4084 }
4085
4086 static FRAME *
4087 delete_framed(FRAME * fp, bool showit)
4088 {
4089     FRAME *np = 0;
4090
4091     if (fp != 0) {
4092         fp->last->next = fp->next;
4093         fp->next->last = fp->last;
4094
4095         if (showit) {
4096             werase(fp->wind);
4097             wrefresh(fp->wind);
4098         }
4099         delwin(fp->wind);
4100
4101         np = (fp == fp->next) ? 0 : fp->next;
4102         free(fp);
4103     }
4104     return np;
4105 }
4106
4107 static void
4108 acs_and_scroll(void)
4109 /* Demonstrate windows */
4110 {
4111     int c;
4112     FRAME *current = (FRAME *) 0, *neww;
4113     WINDOW *usescr = stdscr;
4114 #if HAVE_PUTWIN && HAVE_GETWIN
4115     FILE *fp;
4116 #endif
4117
4118 #define DUMPFILE        "screendump"
4119
4120 #ifdef NCURSES_MOUSE_VERSION
4121     mousemask(BUTTON1_CLICKED, (mmask_t *) 0);
4122 #endif
4123     c = CTRL('C');
4124     raw();
4125     do {
4126         transient((FRAME *) 0, (char *) 0);
4127         switch (c) {
4128         case CTRL('C'):
4129             if ((neww = typeCalloc(FRAME, 1)) == 0) {
4130                 goto breakout;
4131             }
4132             if ((neww->wind = getwindow()) == (WINDOW *) 0) {
4133                 free(neww);
4134                 goto breakout;
4135             }
4136
4137             if (current == 0) { /* First element,  */
4138                 neww->next = neww;      /*   so point it at itself */
4139                 neww->last = neww;
4140             } else {
4141                 neww->next = current->next;
4142                 neww->last = current;
4143                 neww->last->next = neww;
4144                 neww->next->last = neww;
4145             }
4146             current = neww;
4147             /* SVr4 curses sets the keypad on all newly-created windows to
4148              * false.  Someone reported that PDCurses makes new windows inherit
4149              * this flag.  Remove the following 'keypad()' call to test this
4150              */
4151             keypad(current->wind, TRUE);
4152             current->do_keypad = HaveKeypad(current);
4153             current->do_scroll = HaveScroll(current);
4154             break;
4155
4156         case CTRL('N'): /* go to next window */
4157             if (current)
4158                 current = current->next;
4159             break;
4160
4161         case CTRL('P'): /* go to previous window */
4162             if (current)
4163                 current = current->last;
4164             break;
4165
4166         case CTRL('F'): /* scroll current window forward */
4167             if (current)
4168                 wscrl(frame_win(current), 1);
4169             break;
4170
4171         case CTRL('B'): /* scroll current window backwards */
4172             if (current)
4173                 wscrl(frame_win(current), -1);
4174             break;
4175
4176         case CTRL('K'): /* toggle keypad mode for current */
4177             if (current) {
4178                 current->do_keypad = !current->do_keypad;
4179                 keypad(current->wind, current->do_keypad);
4180             }
4181             break;
4182
4183         case CTRL('S'):
4184             if (current) {
4185                 current->do_scroll = !current->do_scroll;
4186                 scrollok(current->wind, current->do_scroll);
4187             }
4188             break;
4189
4190 #if HAVE_PUTWIN && HAVE_GETWIN
4191         case CTRL('W'): /* save and delete window */
4192             if ((current != 0) && (current == current->next)) {
4193                 transient(current, "Will not save/delete ONLY window");
4194                 break;
4195             } else if ((fp = fopen(DUMPFILE, "w")) == (FILE *) 0) {
4196                 transient(current, "Can't open screen dump file");
4197             } else {
4198                 (void) putwin(frame_win(current), fp);
4199                 (void) fclose(fp);
4200
4201                 current = delete_framed(current, TRUE);
4202             }
4203             break;
4204
4205         case CTRL('R'): /* restore window */
4206             if ((fp = fopen(DUMPFILE, "r")) == (FILE *) 0) {
4207                 transient(current, "Can't open screen dump file");
4208             } else {
4209                 if ((neww = typeCalloc(FRAME, 1)) != 0) {
4210
4211                     neww->next = current ? current->next : 0;
4212                     neww->last = current;
4213                     neww->last->next = neww;
4214                     neww->next->last = neww;
4215
4216                     neww->wind = getwin(fp);
4217
4218                     wrefresh(neww->wind);
4219                 }
4220                 (void) fclose(fp);
4221             }
4222             break;
4223 #endif
4224
4225 #if HAVE_WRESIZE
4226         case CTRL('X'): /* resize window */
4227             if (current) {
4228                 pair *tmp, ul, lr;
4229                 int i, mx, my;
4230
4231                 move(0, 0);
4232                 clrtoeol();
4233                 addstr("Use arrows to move cursor, anything else to mark new corner");
4234                 refresh();
4235
4236                 getbegyx(current->wind, ul.y, ul.x);
4237
4238                 tmp = selectcell(ul.y, ul.x, LINES - BOTLINES - 2, COLS - 2);
4239                 if (tmp == (pair *) 0) {
4240                     beep();
4241                     break;
4242                 }
4243
4244                 getmaxyx(current->wind, lr.y, lr.x);
4245                 lr.y += (ul.y - 1);
4246                 lr.x += (ul.x - 1);
4247                 outerbox(ul, lr, FALSE);
4248                 wnoutrefresh(stdscr);
4249
4250                 /* strictly cosmetic hack for the test */
4251                 getmaxyx(current->wind, my, mx);
4252                 if (my > tmp->y - ul.y) {
4253                     getyx(current->wind, lr.y, lr.x);
4254                     wmove(current->wind, tmp->y - ul.y + 1, 0);
4255                     wclrtobot(current->wind);
4256                     wmove(current->wind, lr.y, lr.x);
4257                 }
4258                 if (mx > tmp->x - ul.x)
4259                     for (i = 0; i < my; i++) {
4260                         wmove(current->wind, i, tmp->x - ul.x + 1);
4261                         wclrtoeol(current->wind);
4262                     }
4263                 wnoutrefresh(current->wind);
4264
4265                 memcpy(&lr, tmp, sizeof(pair));
4266                 (void) wresize(current->wind, lr.y - ul.y + 0, lr.x - ul.x + 0);
4267
4268                 getbegyx(current->wind, ul.y, ul.x);
4269                 getmaxyx(current->wind, lr.y, lr.x);
4270                 lr.y += (ul.y - 1);
4271                 lr.x += (ul.x - 1);
4272                 outerbox(ul, lr, TRUE);
4273                 wnoutrefresh(stdscr);
4274
4275                 wnoutrefresh(current->wind);
4276                 move(0, 0);
4277                 clrtoeol();
4278                 doupdate();
4279             }
4280             break;
4281 #endif /* HAVE_WRESIZE */
4282
4283         case KEY_F(10): /* undocumented --- use this to test area clears */
4284             selectcell(0, 0, LINES - 1, COLS - 1);
4285             clrtobot();
4286             refresh();
4287             break;
4288
4289         case KEY_UP:
4290             newwin_move(current, -1, 0);
4291             break;
4292         case KEY_DOWN:
4293             newwin_move(current, 1, 0);
4294             break;
4295         case KEY_LEFT:
4296             newwin_move(current, 0, -1);
4297             break;
4298         case KEY_RIGHT:
4299             newwin_move(current, 0, 1);
4300             break;
4301
4302         case KEY_BACKSPACE:
4303             /* FALLTHROUGH */
4304         case KEY_DC:
4305             {
4306                 int y, x;
4307                 getyx(frame_win(current), y, x);
4308                 if (--x < 0) {
4309                     if (--y < 0)
4310                         break;
4311                     x = getmaxx(frame_win(current)) - 1;
4312                 }
4313                 mvwdelch(frame_win(current), y, x);
4314             }
4315             break;
4316
4317         case '\r':
4318             c = '\n';
4319             /* FALLTHROUGH */
4320
4321         default:
4322             if (current)
4323                 waddch(current->wind, (chtype) c);
4324             else
4325                 beep();
4326             break;
4327         }
4328         newwin_report(current);
4329         usescr = frame_win(current);
4330         wrefresh(usescr);
4331     } while
4332         (!isQuit(c = wGetchar(usescr))
4333          && (c != ERR));
4334
4335   breakout:
4336     while (current != 0)
4337         current = delete_framed(current, FALSE);
4338
4339     scrollok(stdscr, TRUE);     /* reset to driver's default */
4340 #ifdef NCURSES_MOUSE_VERSION
4341     mousemask(0, (mmask_t *) 0);
4342 #endif
4343     noraw();
4344     erase();
4345     endwin();
4346 }
4347
4348 /****************************************************************************
4349  *
4350  * Panels tester
4351  *
4352  ****************************************************************************/
4353
4354 #if USE_LIBPANEL
4355 static int nap_msec = 1;
4356
4357 static NCURSES_CONST char *mod[] =
4358 {
4359     "test ",
4360     "TEST ",
4361     "(**) ",
4362     "*()* ",
4363     "<--> ",
4364     "LAST "
4365 };
4366
4367 /*+-------------------------------------------------------------------------
4368         wait_a_while(msec)
4369 --------------------------------------------------------------------------*/
4370 static void
4371 wait_a_while(int msec GCC_UNUSED)
4372 {
4373 #if HAVE_NAPMS
4374     if (nap_msec == 1)
4375         wGetchar(stdscr);
4376     else
4377         napms(nap_msec);
4378 #else
4379     if (nap_msec == 1)
4380         wGetchar(stdscr);
4381     else if (msec > 1000)
4382         sleep((unsigned) msec / 1000);
4383     else
4384         sleep(1);
4385 #endif
4386 }                               /* end of wait_a_while */
4387
4388 /*+-------------------------------------------------------------------------
4389         saywhat(text)
4390 --------------------------------------------------------------------------*/
4391 static void
4392 saywhat(NCURSES_CONST char *text)
4393 {
4394     wmove(stdscr, LINES - 1, 0);
4395     wclrtoeol(stdscr);
4396     if (text != 0 && *text != '\0') {
4397         waddstr(stdscr, text);
4398         waddstr(stdscr, "; ");
4399     }
4400     waddstr(stdscr, "press any key to continue");
4401 }                               /* end of saywhat */
4402
4403 /*+-------------------------------------------------------------------------
4404         mkpanel(rows,cols,tly,tlx) - alloc a win and panel and associate them
4405 --------------------------------------------------------------------------*/
4406 static PANEL *
4407 mkpanel(short color, int rows, int cols, int tly, int tlx)
4408 {
4409     WINDOW *win;
4410     PANEL *pan = 0;
4411
4412     if ((win = newwin(rows, cols, tly, tlx)) != 0) {
4413         if ((pan = new_panel(win)) == 0) {
4414             delwin(win);
4415         } else if (use_colors) {
4416             short fg = (short) ((color == COLOR_BLUE) ? COLOR_WHITE : COLOR_BLACK);
4417             short bg = color;
4418
4419             init_pair(color, fg, bg);
4420             wbkgdset(win, (chtype) (COLOR_PAIR(color) | ' '));
4421         } else {
4422             wbkgdset(win, A_BOLD | ' ');
4423         }
4424     }
4425     return pan;
4426 }                               /* end of mkpanel */
4427
4428 /*+-------------------------------------------------------------------------
4429         rmpanel(pan)
4430 --------------------------------------------------------------------------*/
4431 static void
4432 rmpanel(PANEL * pan)
4433 {
4434     WINDOW *win = panel_window(pan);
4435     del_panel(pan);
4436     delwin(win);
4437 }                               /* end of rmpanel */
4438
4439 /*+-------------------------------------------------------------------------
4440         pflush()
4441 --------------------------------------------------------------------------*/
4442 static void
4443 pflush(void)
4444 {
4445     update_panels();
4446     doupdate();
4447 }                               /* end of pflush */
4448
4449 /*+-------------------------------------------------------------------------
4450         fill_panel(win)
4451 --------------------------------------------------------------------------*/
4452 static void
4453 init_panel(void)
4454 {
4455     register int y, x;
4456
4457     for (y = 0; y < LINES - 1; y++) {
4458         for (x = 0; x < COLS; x++)
4459             wprintw(stdscr, "%d", (y + x) % 10);
4460     }
4461 }
4462
4463 static void
4464 fill_panel(PANEL * pan)
4465 {
4466     WINDOW *win = panel_window(pan);
4467     const char *userptr = (const char *) panel_userptr(pan);
4468     int num = (userptr && *userptr) ? userptr[1] : '?';
4469     int y, x;
4470
4471     wmove(win, 1, 1);
4472     wprintw(win, "-pan%c-", num);
4473     wclrtoeol(win);
4474     box(win, 0, 0);
4475     for (y = 2; y < getmaxy(win) - 1; y++) {
4476         for (x = 1; x < getmaxx(win) - 1; x++) {
4477             wmove(win, y, x);
4478             waddch(win, UChar(num));
4479         }
4480     }
4481 }
4482
4483 #if USE_WIDEC_SUPPORT
4484 static void
4485 init_wide_panel(void)
4486 {
4487     int digit;
4488     cchar_t temp[10];
4489
4490     for (digit = 0; digit < 10; ++digit)
4491         make_fullwidth_digit(&temp[digit], digit);
4492
4493     do {
4494         int y, x;
4495         getyx(stdscr, y, x);
4496         digit = (y + x / 2) % 10;
4497     } while (add_wch(&temp[digit]) != ERR);
4498 }
4499
4500 static void
4501 fill_wide_panel(PANEL * pan)
4502 {
4503     WINDOW *win = panel_window(pan);
4504     const char *userptr = (const char *) panel_userptr(pan);
4505     int num = (userptr && *userptr) ? userptr[1] : '?';
4506     int y, x;
4507
4508     wmove(win, 1, 1);
4509     wprintw(win, "-pan%c-", num);
4510     wclrtoeol(win);
4511     box(win, 0, 0);
4512     for (y = 2; y < getmaxy(win) - 1; y++) {
4513         for (x = 1; x < getmaxx(win) - 1; x++) {
4514             wmove(win, y, x);
4515             waddch(win, UChar(num));
4516         }
4517     }
4518 }
4519 #endif
4520
4521 #define MAX_PANELS 5
4522
4523 static void
4524 canned_panel(PANEL * px[MAX_PANELS + 1], NCURSES_CONST char *cmd)
4525 {
4526     int which = cmd[1] - '0';
4527
4528     saywhat(cmd);
4529     switch (*cmd) {
4530     case 'h':
4531         hide_panel(px[which]);
4532         break;
4533     case 's':
4534         show_panel(px[which]);
4535         break;
4536     case 't':
4537         top_panel(px[which]);
4538         break;
4539     case 'b':
4540         bottom_panel(px[which]);
4541         break;
4542     case 'd':
4543         rmpanel(px[which]);
4544         break;
4545     }
4546     pflush();
4547     wait_a_while(nap_msec);
4548 }
4549
4550 static void
4551 demo_panels(void (*InitPanel) (void), void (*FillPanel) (PANEL *))
4552 {
4553     int count;
4554     int itmp;
4555     PANEL *px[MAX_PANELS + 1];
4556
4557     scrollok(stdscr, FALSE);    /* we don't want stdscr to scroll! */
4558     refresh();
4559
4560     InitPanel();
4561     for (count = 0; count < 5; count++) {
4562         px[1] = mkpanel(COLOR_RED,
4563                         LINES / 2 - 2,
4564                         COLS / 8 + 1,
4565                         0,
4566                         0);
4567         set_panel_userptr(px[1], (NCURSES_CONST void *) "p1");
4568
4569         px[2] = mkpanel(COLOR_GREEN,
4570                         LINES / 2 + 1,
4571                         COLS / 7,
4572                         LINES / 4,
4573                         COLS / 10);
4574         set_panel_userptr(px[2], (NCURSES_CONST void *) "p2");
4575
4576         px[3] = mkpanel(COLOR_YELLOW,
4577                         LINES / 4,
4578                         COLS / 10,
4579                         LINES / 2,
4580                         COLS / 9);
4581         set_panel_userptr(px[3], (NCURSES_CONST void *) "p3");
4582
4583         px[4] = mkpanel(COLOR_BLUE,
4584                         LINES / 2 - 2,
4585                         COLS / 8,
4586                         LINES / 2 - 2,
4587                         COLS / 3);
4588         set_panel_userptr(px[4], (NCURSES_CONST void *) "p4");
4589
4590         px[5] = mkpanel(COLOR_MAGENTA,
4591                         LINES / 2 - 2,
4592                         COLS / 8,
4593                         LINES / 2,
4594                         COLS / 2 - 2);
4595         set_panel_userptr(px[5], (NCURSES_CONST void *) "p5");
4596
4597         FillPanel(px[1]);
4598         FillPanel(px[2]);
4599         FillPanel(px[3]);
4600         FillPanel(px[4]);
4601         FillPanel(px[5]);
4602
4603         hide_panel(px[4]);
4604         hide_panel(px[5]);
4605         pflush();
4606         saywhat("");
4607         wait_a_while(nap_msec);
4608
4609         saywhat("h3 s1 s2 s4 s5");
4610         move_panel(px[1], 0, 0);
4611         hide_panel(px[3]);
4612         show_panel(px[1]);
4613         show_panel(px[2]);
4614         show_panel(px[4]);
4615         show_panel(px[5]);
4616         pflush();
4617         wait_a_while(nap_msec);
4618
4619         canned_panel(px, "s1");
4620         canned_panel(px, "s2");
4621
4622         saywhat("m2");
4623         move_panel(px[2], LINES / 3 + 1, COLS / 8);
4624         pflush();
4625         wait_a_while(nap_msec);
4626
4627         canned_panel(px, "s3");
4628
4629         saywhat("m3");
4630         move_panel(px[3], LINES / 4 + 1, COLS / 15);
4631         pflush();
4632         wait_a_while(nap_msec);
4633
4634         canned_panel(px, "b3");
4635         canned_panel(px, "s4");
4636         canned_panel(px, "s5");
4637         canned_panel(px, "t3");
4638         canned_panel(px, "t1");
4639         canned_panel(px, "t2");
4640         canned_panel(px, "t3");
4641         canned_panel(px, "t4");
4642
4643         for (itmp = 0; itmp < 6; itmp++) {
4644             WINDOW *w4 = panel_window(px[4]);
4645             WINDOW *w5 = panel_window(px[5]);
4646
4647             saywhat("m4");
4648             wmove(w4, LINES / 8, 1);
4649             waddstr(w4, mod[itmp]);
4650             move_panel(px[4], LINES / 6, itmp * (COLS / 8));
4651             wmove(w5, LINES / 6, 1);
4652             waddstr(w5, mod[itmp]);
4653             pflush();
4654             wait_a_while(nap_msec);
4655
4656             saywhat("m5");
4657             wmove(w4, LINES / 6, 1);
4658             waddstr(w4, mod[itmp]);
4659             move_panel(px[5], LINES / 3 - 1, (itmp * 10) + 6);
4660             wmove(w5, LINES / 8, 1);
4661             waddstr(w5, mod[itmp]);
4662             pflush();
4663             wait_a_while(nap_msec);
4664         }
4665
4666         saywhat("m4");
4667         move_panel(px[4], LINES / 6, itmp * (COLS / 8));
4668         pflush();
4669         wait_a_while(nap_msec);
4670
4671         canned_panel(px, "t5");
4672         canned_panel(px, "t2");
4673         canned_panel(px, "t1");
4674         canned_panel(px, "d2");
4675         canned_panel(px, "h3");
4676         canned_panel(px, "d1");
4677         canned_panel(px, "d4");
4678         canned_panel(px, "d5");
4679         canned_panel(px, "d3");
4680
4681         wait_a_while(nap_msec);
4682         if (nap_msec == 1)
4683             break;
4684         nap_msec = 100L;
4685     }
4686
4687     erase();
4688     endwin();
4689 }
4690 #endif /* USE_LIBPANEL */
4691
4692 /****************************************************************************
4693  *
4694  * Pad tester
4695  *
4696  ****************************************************************************/
4697
4698 #define GRIDSIZE        3
4699
4700 static bool pending_pan = FALSE;
4701 static bool show_panner_legend = TRUE;
4702
4703 static int
4704 panner_legend(int line)
4705 {
4706     static const char *const legend[] =
4707     {
4708         "Use arrow keys (or U,D,L,R) to pan, ESC to quit, ! to shell-out.",
4709         "Use +,- (or j,k) to grow/shrink the panner vertically.",
4710         "Use <,> (or h,l) to grow/shrink the panner horizontally.",
4711         "Number repeats.  Toggle legend:? filler:a timer:t scrollmark:s."
4712     };
4713     int n = ((int) SIZEOF(legend) - (LINES - line));
4714     if (line < LINES && (n >= 0)) {
4715         move(line, 0);
4716         if (show_panner_legend)
4717             printw("%s", legend[n]);
4718         clrtoeol();
4719         return show_panner_legend;
4720     }
4721     return FALSE;
4722 }
4723
4724 static void
4725 panner_h_cleanup(int from_y, int from_x, int to_x)
4726 {
4727     if (!panner_legend(from_y))
4728         do_h_line(from_y, from_x, ' ', to_x);
4729 }
4730
4731 static void
4732 panner_v_cleanup(int from_y, int from_x, int to_y)
4733 {
4734     if (!panner_legend(from_y))
4735         do_v_line(from_y, from_x, ' ', to_y);
4736 }
4737
4738 static void
4739 fill_pad(WINDOW *panpad, bool pan_lines)
4740 {
4741     int y, x;
4742     unsigned gridcount = 0;
4743
4744     wmove(panpad, 0, 0);
4745     for (y = 0; y < getmaxy(panpad); y++) {
4746         for (x = 0; x < getmaxx(panpad); x++) {
4747             if (y % GRIDSIZE == 0 && x % GRIDSIZE == 0) {
4748                 if (y == 0 && x == 0)
4749                     waddch(panpad, pan_lines ? ACS_ULCORNER : '+');
4750                 else if (y == 0)
4751                     waddch(panpad, pan_lines ? ACS_TTEE : '+');
4752                 else if (y == 0 || x == 0)
4753                     waddch(panpad, pan_lines ? ACS_LTEE : '+');
4754                 else
4755                     waddch(panpad, (chtype) ((pan_lines ? 'a' : 'A') +
4756                                              (int) (gridcount++ % 26)));
4757             } else if (y % GRIDSIZE == 0)
4758                 waddch(panpad, pan_lines ? ACS_HLINE : '-');
4759             else if (x % GRIDSIZE == 0)
4760                 waddch(panpad, pan_lines ? ACS_VLINE : '|');
4761             else
4762                 waddch(panpad, ' ');
4763         }
4764     }
4765 }
4766
4767 static void
4768 panner(WINDOW *pad,
4769        int top_x, int top_y, int porty, int portx,
4770        int (*pgetc) (WINDOW *))
4771 {
4772 #if HAVE_GETTIMEOFDAY
4773     struct timeval before, after;
4774     bool timing = TRUE;
4775 #endif
4776     bool pan_lines = FALSE;
4777     bool scrollers = TRUE;
4778     int basex = 0;
4779     int basey = 0;
4780     int pxmax, pymax, lowend, highend, c;
4781
4782     getmaxyx(pad, pymax, pxmax);
4783     scrollok(stdscr, FALSE);    /* we don't want stdscr to scroll! */
4784
4785     c = KEY_REFRESH;
4786     do {
4787 #ifdef NCURSES_VERSION
4788         /*
4789          * During shell-out, the user may have resized the window.  Adjust
4790          * the port size of the pad to accommodate this.  Ncurses automatically
4791          * resizes all of the normal windows to fit on the new screen.
4792          */
4793         if (top_x > COLS)
4794             top_x = COLS;
4795         if (portx > COLS)
4796             portx = COLS;
4797         if (top_y > LINES)
4798             top_y = LINES;
4799         if (porty > LINES)
4800             porty = LINES;
4801 #endif
4802         switch (c) {
4803         case KEY_REFRESH:
4804             erase();
4805
4806             /* FALLTHRU */
4807         case '?':
4808             if (c == '?')
4809                 show_panner_legend = !show_panner_legend;
4810             panner_legend(LINES - 4);
4811             panner_legend(LINES - 3);
4812             panner_legend(LINES - 2);
4813             panner_legend(LINES - 1);
4814             break;
4815         case 'a':
4816             pan_lines = !pan_lines;
4817             fill_pad(pad, pan_lines);
4818             pending_pan = FALSE;
4819             break;
4820
4821 #if HAVE_GETTIMEOFDAY
4822         case 't':
4823             timing = !timing;
4824             if (!timing)
4825                 panner_legend(LINES - 1);
4826             break;
4827 #endif
4828         case 's':
4829             scrollers = !scrollers;
4830             break;
4831
4832             /* Move the top-left corner of the pad, keeping the bottom-right
4833              * corner fixed.
4834              */
4835         case 'h':               /* increase-columns: move left edge to left */
4836             if (top_x <= 0)
4837                 beep();
4838             else {
4839                 panner_v_cleanup(top_y, top_x, porty);
4840                 top_x--;
4841             }
4842             break;
4843
4844         case 'j':               /* decrease-lines: move top-edge down */
4845             if (top_y >= porty)
4846                 beep();
4847             else {
4848                 panner_h_cleanup(top_y - 1, top_x - (top_x > 0), portx);
4849                 top_y++;
4850             }
4851             break;
4852
4853         case 'k':               /* increase-lines: move top-edge up */
4854             if (top_y <= 0)
4855                 beep();
4856             else {
4857                 top_y--;
4858                 panner_h_cleanup(top_y, top_x, portx);
4859             }
4860             break;
4861
4862         case 'l':               /* decrease-columns: move left-edge to right */
4863             if (top_x >= portx)
4864                 beep();
4865             else {
4866                 panner_v_cleanup(top_y - (top_y > 0), top_x - 1, porty);
4867                 top_x++;
4868             }
4869             break;
4870
4871             /* Move the bottom-right corner of the pad, keeping the top-left
4872              * corner fixed.
4873              */
4874         case KEY_IC:            /* increase-columns: move right-edge to right */
4875             if (portx >= pxmax || portx >= COLS)
4876                 beep();
4877             else {
4878                 panner_v_cleanup(top_y - (top_y > 0), portx - 1, porty);
4879                 ++portx;
4880             }
4881             break;
4882
4883         case KEY_IL:            /* increase-lines: move bottom-edge down */
4884             if (porty >= pymax || porty >= LINES)
4885                 beep();
4886             else {
4887                 panner_h_cleanup(porty - 1, top_x - (top_x > 0), portx);
4888                 ++porty;
4889             }
4890             break;
4891
4892         case KEY_DC:            /* decrease-columns: move bottom edge up */
4893             if (portx <= top_x)
4894                 beep();
4895             else {
4896                 portx--;
4897                 panner_v_cleanup(top_y - (top_y > 0), portx, porty);
4898             }
4899             break;
4900
4901         case KEY_DL:            /* decrease-lines */
4902             if (porty <= top_y)
4903                 beep();
4904             else {
4905                 porty--;
4906                 panner_h_cleanup(porty, top_x - (top_x > 0), portx);
4907             }
4908             break;
4909
4910         case KEY_LEFT:          /* pan leftwards */
4911             if (basex > 0)
4912                 basex--;
4913             else
4914                 beep();
4915             break;
4916
4917         case KEY_RIGHT: /* pan rightwards */
4918             if (basex + portx - (pymax > porty) < pxmax)
4919                 basex++;
4920             else
4921                 beep();
4922             break;
4923
4924         case KEY_UP:            /* pan upwards */
4925             if (basey > 0)
4926                 basey--;
4927             else
4928                 beep();
4929             break;
4930
4931         case KEY_DOWN:          /* pan downwards */
4932             if (basey + porty - (pxmax > portx) < pymax)
4933                 basey++;
4934             else
4935                 beep();
4936             break;
4937
4938         case 'H':
4939         case KEY_HOME:
4940         case KEY_FIND:
4941             basey = 0;
4942             break;
4943
4944         case 'E':
4945         case KEY_END:
4946         case KEY_SELECT:
4947             basey = pymax - porty;
4948             if (basey < 0)
4949                 basey = 0;
4950             break;
4951
4952         default:
4953             beep();
4954             break;
4955         }
4956
4957         mvaddch(top_y - 1, top_x - 1, ACS_ULCORNER);
4958         do_v_line(top_y, top_x - 1, ACS_VLINE, porty);
4959         do_h_line(top_y - 1, top_x, ACS_HLINE, portx);
4960
4961         if (scrollers && (pxmax > portx - 1)) {
4962             int length = (portx - top_x - 1);
4963             float ratio = ((float) length) / ((float) pxmax);
4964
4965             lowend = (int) ((float) top_x + ((float) basex * ratio));
4966             highend = (int) ((float) top_x + ((float) (basex + length) * ratio));
4967
4968             do_h_line(porty - 1, top_x, ACS_HLINE, lowend);
4969             if (highend < portx) {
4970                 attron(A_REVERSE);
4971                 do_h_line(porty - 1, lowend, ' ', highend + 1);
4972                 attroff(A_REVERSE);
4973                 do_h_line(porty - 1, highend + 1, ACS_HLINE, portx);
4974             }
4975         } else
4976             do_h_line(porty - 1, top_x, ACS_HLINE, portx);
4977
4978         if (scrollers && (pymax > porty - 1)) {
4979             int length = (porty - top_y - 1);
4980             float ratio = ((float) length) / ((float) pymax);
4981
4982             lowend = (int) ((float) top_y + ((float) basey * ratio));
4983             highend = (int) ((float) top_y + ((float) (basey + length) * ratio));
4984
4985             do_v_line(top_y, portx - 1, ACS_VLINE, lowend);
4986             if (highend < porty) {
4987                 attron(A_REVERSE);
4988                 do_v_line(lowend, portx - 1, ' ', highend + 1);
4989                 attroff(A_REVERSE);
4990                 do_v_line(highend + 1, portx - 1, ACS_VLINE, porty);
4991             }
4992         } else
4993             do_v_line(top_y, portx - 1, ACS_VLINE, porty);
4994
4995         mvaddch(top_y - 1, portx - 1, ACS_URCORNER);
4996         mvaddch(porty - 1, top_x - 1, ACS_LLCORNER);
4997         mvaddch(porty - 1, portx - 1, ACS_LRCORNER);
4998
4999         if (!pending_pan) {
5000 #if HAVE_GETTIMEOFDAY
5001             gettimeofday(&before, 0);
5002 #endif
5003             wnoutrefresh(stdscr);
5004
5005             pnoutrefresh(pad,
5006                          basey, basex,
5007                          top_y, top_x,
5008                          porty - (pxmax > portx) - 1,
5009                          portx - (pymax > porty) - 1);
5010
5011             doupdate();
5012 #if HAVE_GETTIMEOFDAY
5013             if (timing) {
5014                 double elapsed;
5015                 gettimeofday(&after, 0);
5016                 elapsed = (after.tv_sec + after.tv_usec / 1.0e6)
5017                     - (before.tv_sec + before.tv_usec / 1.0e6);
5018                 move(LINES - 1, COLS - 12);
5019                 printw("Secs: %2.03f", elapsed);
5020                 refresh();
5021             }
5022 #endif
5023         }
5024
5025     } while
5026         ((c = pgetc(pad)) != KEY_EXIT);
5027
5028     scrollok(stdscr, TRUE);     /* reset to driver's default */
5029 }
5030
5031 static int
5032 padgetch(WINDOW *win)
5033 {
5034     static int count;
5035     static int last;
5036     int c;
5037
5038     if ((pending_pan = (count > 0)) != FALSE) {
5039         count--;
5040         pending_pan = (count != 0);
5041     } else {
5042         for (;;) {
5043             switch (c = wGetchar(win)) {
5044             case '!':
5045                 ShellOut(FALSE);
5046                 /* FALLTHRU */
5047             case CTRL('r'):
5048                 endwin();
5049                 refresh();
5050                 c = KEY_REFRESH;
5051                 break;
5052             case CTRL('l'):
5053                 c = KEY_REFRESH;
5054                 break;
5055             case 'U':
5056                 c = KEY_UP;
5057                 break;
5058             case 'D':
5059                 c = KEY_DOWN;
5060                 break;
5061             case 'R':
5062                 c = KEY_RIGHT;
5063                 break;
5064             case 'L':
5065                 c = KEY_LEFT;
5066                 break;
5067             case '+':
5068                 c = KEY_IL;
5069                 break;
5070             case '-':
5071                 c = KEY_DL;
5072                 break;
5073             case '>':
5074                 c = KEY_IC;
5075                 break;
5076             case '<':
5077                 c = KEY_DC;
5078                 break;
5079             case ERR:           /* FALLTHRU */
5080             case case_QUIT:
5081                 count = 0;
5082                 c = KEY_EXIT;
5083                 break;
5084             default:
5085                 if (c >= '0' && c <= '9') {
5086                     count = count * 10 + (c - '0');
5087                     continue;
5088                 }
5089                 break;
5090             }
5091             last = c;
5092             break;
5093         }
5094         if (count > 0)
5095             count--;
5096     }
5097     return (last);
5098 }
5099
5100 #define PAD_HIGH 200
5101 #define PAD_WIDE 200
5102
5103 static void
5104 demo_pad(void)
5105 /* Demonstrate pads. */
5106 {
5107     WINDOW *panpad = newpad(PAD_HIGH, PAD_WIDE);
5108
5109     if (panpad == 0) {
5110         Cannot("cannot create requested pad");
5111         return;
5112     }
5113
5114     fill_pad(panpad, FALSE);
5115
5116     panner_legend(LINES - 4);
5117     panner_legend(LINES - 3);
5118     panner_legend(LINES - 2);
5119     panner_legend(LINES - 1);
5120
5121     keypad(panpad, TRUE);
5122
5123     /* Make the pad (initially) narrow enough that a trace file won't wrap.
5124      * We'll still be able to widen it during a test, since that's required
5125      * for testing boundaries.
5126      */
5127     panner(panpad, 2, 2, LINES - 5, COLS - 15, padgetch);
5128
5129     delwin(panpad);
5130     endwin();
5131     erase();
5132 }
5133
5134 /****************************************************************************
5135  *
5136  * Tests from John Burnell's PDCurses tester
5137  *
5138  ****************************************************************************/
5139
5140 static void
5141 Continue(WINDOW *win)
5142 {
5143     noecho();
5144     wmove(win, 10, 1);
5145     mvwaddstr(win, 10, 1, " Press any key to continue");
5146     wrefresh(win);
5147     wGetchar(win);
5148 }
5149
5150 static void
5151 flushinp_test(WINDOW *win)
5152 /* Input test, adapted from John Burnell's PDCurses tester */
5153 {
5154     int w, h, bx, by, sw, sh, i;
5155
5156     WINDOW *subWin;
5157     wclear(win);
5158
5159     getmaxyx(win, h, w);
5160     getbegyx(win, by, bx);
5161     sw = w / 3;
5162     sh = h / 3;
5163     if ((subWin = subwin(win, sh, sw, by + h - sh - 2, bx + w - sw - 2)) == 0)
5164         return;
5165
5166 #ifdef A_COLOR
5167     if (use_colors) {
5168         init_pair(2, COLOR_CYAN, COLOR_BLUE);
5169         wbkgd(subWin, COLOR_PAIR(2) | ' ');
5170     }
5171 #endif
5172     (void) wattrset(subWin, A_BOLD);
5173     box(subWin, ACS_VLINE, ACS_HLINE);
5174     mvwaddstr(subWin, 2, 1, "This is a subwindow");
5175     wrefresh(win);
5176
5177     /*
5178      * This used to set 'nocbreak()'.  However, Alexander Lukyanov says that
5179      * it only happened to "work" on SVr4 because that implementation does not
5180      * emulate nocbreak+noecho mode, whereas ncurses does.  To get the desired
5181      * test behavior, we're using 'cbreak()', which will allow a single
5182      * character to return without needing a newline. - T.Dickey 1997/10/11.
5183      */
5184     cbreak();
5185     mvwaddstr(win, 0, 1, "This is a test of the flushinp() call.");
5186
5187     mvwaddstr(win, 2, 1, "Type random keys for 5 seconds.");
5188     mvwaddstr(win, 3, 1,
5189               "These should be discarded (not echoed) after the subwindow goes away.");
5190     wrefresh(win);
5191
5192     for (i = 0; i < 5; i++) {
5193         mvwprintw(subWin, 1, 1, "Time = %d", i);
5194         wrefresh(subWin);
5195         napms(1000);
5196         flushinp();
5197     }
5198
5199     delwin(subWin);
5200     werase(win);
5201     flash();
5202     wrefresh(win);
5203     napms(1000);
5204
5205     mvwaddstr(win, 2, 1,
5206               "If you were still typing when the window timer expired,");
5207     mvwaddstr(win, 3, 1,
5208               "or else you typed nothing at all while it was running,");
5209     mvwaddstr(win, 4, 1,
5210               "test was invalid.  You'll see garbage or nothing at all. ");
5211     mvwaddstr(win, 6, 1, "Press a key");
5212     wmove(win, 9, 10);
5213     wrefresh(win);
5214     echo();
5215     wGetchar(win);
5216     flushinp();
5217     mvwaddstr(win, 12, 0,
5218               "If you see any key other than what you typed, flushinp() is broken.");
5219     Continue(win);
5220
5221     wmove(win, 9, 10);
5222     wdelch(win);
5223     wrefresh(win);
5224     wmove(win, 12, 0);
5225     clrtoeol();
5226     waddstr(win,
5227             "What you typed should now have been deleted; if not, wdelch() failed.");
5228     Continue(win);
5229
5230     cbreak();
5231 }
5232
5233 /****************************************************************************
5234  *
5235  * Menu test
5236  *
5237  ****************************************************************************/
5238
5239 #if USE_LIBMENU
5240
5241 #define MENU_Y  8
5242 #define MENU_X  8
5243
5244 static int
5245 menu_virtualize(int c)
5246 {
5247     if (c == '\n' || c == KEY_EXIT)
5248         return (MAX_COMMAND + 1);
5249     else if (c == 'u')
5250         return (REQ_SCR_ULINE);
5251     else if (c == 'd')
5252         return (REQ_SCR_DLINE);
5253     else if (c == 'b' || c == KEY_NPAGE)
5254         return (REQ_SCR_UPAGE);
5255     else if (c == 'f' || c == KEY_PPAGE)
5256         return (REQ_SCR_DPAGE);
5257     else if (c == 'n' || c == KEY_DOWN)
5258         return (REQ_NEXT_ITEM);
5259     else if (c == 'p' || c == KEY_UP)
5260         return (REQ_PREV_ITEM);
5261     else if (c == ' ')
5262         return (REQ_TOGGLE_ITEM);
5263     else {
5264         if (c != KEY_MOUSE)
5265             beep();
5266         return (c);
5267     }
5268 }
5269
5270 static CONST_MENUS char *animals[] =
5271 {
5272     "Lions",
5273     "Tigers",
5274     "Bears",
5275     "(Oh my!)",
5276     "Newts",
5277     "Platypi",
5278     "Lemurs",
5279     "(Oh really?!)",
5280     "Leopards",
5281     "Panthers",
5282     "Pumas",
5283     "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
5284     "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs, Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
5285     (char *) 0
5286 };
5287
5288 static void
5289 menu_test(void)
5290 {
5291     MENU *m;
5292     ITEM *items[SIZEOF(animals)];
5293     ITEM **ip = items;
5294     CONST_MENUS char **ap;
5295     int mrows, mcols, c;
5296     WINDOW *menuwin;
5297
5298 #ifdef NCURSES_MOUSE_VERSION
5299     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
5300 #endif
5301     mvaddstr(0, 0, "This is the menu test:");
5302     mvaddstr(2, 0, "  Use up and down arrow to move the select bar.");
5303     mvaddstr(3, 0, "  'n' and 'p' act like arrows.");
5304     mvaddstr(4, 0,
5305              "  'b' and 'f' scroll up/down (page), 'u' and 'd' (line).");
5306     mvaddstr(5, 0, "  Press return to exit.");
5307     refresh();
5308
5309     for (ap = animals; *ap; ap++) {
5310         if ((*ip = new_item(*ap, "")) != 0)
5311             ++ip;
5312     }
5313     *ip = (ITEM *) 0;
5314
5315     m = new_menu(items);
5316
5317     set_menu_format(m, (SIZEOF(animals) + 1) / 2, 1);
5318     scale_menu(m, &mrows, &mcols);
5319
5320     menuwin = newwin(mrows + 2, mcols + 2, MENU_Y, MENU_X);
5321     set_menu_win(m, menuwin);
5322     keypad(menuwin, TRUE);
5323     box(menuwin, 0, 0);
5324
5325     set_menu_sub(m, derwin(menuwin, mrows, mcols, 1, 1));
5326
5327     post_menu(m);
5328
5329     while ((c = menu_driver(m, menu_virtualize(wGetchar(menuwin)))) != E_UNKNOWN_COMMAND) {
5330         if (c == E_NOT_POSTED)
5331             break;
5332         if (c == E_REQUEST_DENIED)
5333             beep();
5334         continue;
5335     }
5336
5337     (void) mvprintw(LINES - 2, 0,
5338                     "You chose: %s\n", item_name(current_item(m)));
5339     (void) addstr("Press any key to continue...");
5340     wGetchar(stdscr);
5341
5342     unpost_menu(m);
5343     delwin(menuwin);
5344
5345     free_menu(m);
5346     for (ip = items; *ip; ip++)
5347         free_item(*ip);
5348 #ifdef NCURSES_MOUSE_VERSION
5349     mousemask(0, (mmask_t *) 0);
5350 #endif
5351 }
5352
5353 #ifdef TRACE
5354 #define T_TBL(name) { #name, name }
5355 static struct {
5356     const char *name;
5357     unsigned mask;
5358 } t_tbl[] = {
5359
5360     T_TBL(TRACE_DISABLE),
5361         T_TBL(TRACE_TIMES),
5362         T_TBL(TRACE_TPUTS),
5363         T_TBL(TRACE_UPDATE),
5364         T_TBL(TRACE_MOVE),
5365         T_TBL(TRACE_CHARPUT),
5366         T_TBL(TRACE_ORDINARY),
5367         T_TBL(TRACE_CALLS),
5368         T_TBL(TRACE_VIRTPUT),
5369         T_TBL(TRACE_IEVENT),
5370         T_TBL(TRACE_BITS),
5371         T_TBL(TRACE_ICALLS),
5372         T_TBL(TRACE_CCALLS),
5373         T_TBL(TRACE_DATABASE),
5374         T_TBL(TRACE_ATTRS),
5375         T_TBL(TRACE_MAXIMUM),
5376     {
5377         (char *) 0, 0
5378     }
5379 };
5380
5381 static char *
5382 tracetrace(unsigned tlevel)
5383 {
5384     static char *buf;
5385     int n;
5386
5387     if (buf == 0) {
5388         size_t need = 12;
5389         for (n = 0; t_tbl[n].name != 0; n++)
5390             need += strlen(t_tbl[n].name) + 2;
5391         buf = typeMalloc(char, need);
5392     }
5393     sprintf(buf, "0x%02x = {", tlevel);
5394     if (tlevel == 0) {
5395         sprintf(buf + strlen(buf), "%s, ", t_tbl[0].name);
5396     } else {
5397         for (n = 1; t_tbl[n].name != 0; n++)
5398             if ((tlevel & t_tbl[n].mask) == t_tbl[n].mask) {
5399                 strcat(buf, t_tbl[n].name);
5400                 strcat(buf, ", ");
5401             }
5402     }
5403     if (buf[strlen(buf) - 2] == ',')
5404         buf[strlen(buf) - 2] = '\0';
5405     return (strcat(buf, "}"));
5406 }
5407
5408 /* fake a dynamically reconfigurable menu using the 0th entry to deselect
5409  * the others
5410  */
5411 static int
5412 run_trace_menu(MENU * m)
5413 {
5414     ITEM **items;
5415     ITEM *i, **p;
5416
5417     for (;;) {
5418         bool changed = FALSE;
5419         switch (menu_driver(m, menu_virtualize(wGetchar(menu_win(m))))) {
5420         case E_UNKNOWN_COMMAND:
5421             return FALSE;
5422         default:
5423             items = menu_items(m);
5424             i = current_item(m);
5425             if (i == items[0]) {
5426                 if (item_value(i)) {
5427                     for (p = items + 1; *p != 0; p++)
5428                         if (item_value(*p)) {
5429                             set_item_value(*p, FALSE);
5430                             changed = TRUE;
5431                         }
5432                 }
5433             } else {
5434                 for (p = items + 1; *p != 0; p++)
5435                     if (item_value(*p)) {
5436                         set_item_value(items[0], FALSE);
5437                         changed = TRUE;
5438                         break;
5439                     }
5440             }
5441             if (!changed)
5442                 return TRUE;
5443         }
5444     }
5445 }
5446
5447 static void
5448 trace_set(void)
5449 /* interactively set the trace level */
5450 {
5451     MENU *m;
5452     ITEM *items[SIZEOF(t_tbl)];
5453     ITEM **ip = items;
5454     int mrows, mcols;
5455     unsigned newtrace;
5456     int n;
5457     WINDOW *menuwin;
5458
5459     mvaddstr(0, 0, "Interactively set trace level:");
5460     mvaddstr(2, 0, "  Press space bar to toggle a selection.");
5461     mvaddstr(3, 0, "  Use up and down arrow to move the select bar.");
5462     mvaddstr(4, 0, "  Press return to set the trace level.");
5463     mvprintw(6, 0, "(Current trace level is %s)", tracetrace(_nc_tracing));
5464
5465     refresh();
5466
5467     for (n = 0; t_tbl[n].name != 0; n++) {
5468         if ((*ip = new_item(t_tbl[n].name, "")) != 0) {
5469             ++ip;
5470         }
5471     }
5472     *ip = (ITEM *) 0;
5473
5474     m = new_menu(items);
5475
5476     set_menu_format(m, 0, 2);
5477     scale_menu(m, &mrows, &mcols);
5478
5479     menu_opts_off(m, O_ONEVALUE);
5480     menuwin = newwin(mrows + 2, mcols + 2, MENU_Y, MENU_X);
5481     set_menu_win(m, menuwin);
5482     keypad(menuwin, TRUE);
5483     box(menuwin, 0, 0);
5484
5485     set_menu_sub(m, derwin(menuwin, mrows, mcols, 1, 1));
5486
5487     post_menu(m);
5488
5489     for (ip = menu_items(m); *ip; ip++) {
5490         unsigned mask = t_tbl[item_index(*ip)].mask;
5491         if (mask == 0)
5492             set_item_value(*ip, _nc_tracing == 0);
5493         else if ((mask & _nc_tracing) == mask)
5494             set_item_value(*ip, TRUE);
5495     }
5496
5497     while (run_trace_menu(m))
5498         continue;
5499
5500     newtrace = 0;
5501     for (ip = menu_items(m); *ip; ip++)
5502         if (item_value(*ip))
5503             newtrace |= t_tbl[item_index(*ip)].mask;
5504     trace(newtrace);
5505     Trace(("trace level interactively set to %s", tracetrace(_nc_tracing)));
5506
5507     (void) mvprintw(LINES - 2, 0,
5508                     "Trace level is %s\n", tracetrace(_nc_tracing));
5509     (void) addstr("Press any key to continue...");
5510     wGetchar(stdscr);
5511
5512     unpost_menu(m);
5513     delwin(menuwin);
5514
5515     free_menu(m);
5516     for (ip = items; *ip; ip++)
5517         free_item(*ip);
5518 }
5519 #endif /* TRACE */
5520 #endif /* USE_LIBMENU */
5521
5522 /****************************************************************************
5523  *
5524  * Forms test
5525  *
5526  ****************************************************************************/
5527 #if USE_LIBFORM
5528 static FIELD *
5529 make_label(int frow, int fcol, NCURSES_CONST char *label)
5530 {
5531     FIELD *f = new_field(1, (int) strlen(label), frow, fcol, 0, 0);
5532
5533     if (f) {
5534         set_field_buffer(f, 0, label);
5535         set_field_opts(f, (int) (field_opts(f) & ~O_ACTIVE));
5536     }
5537     return (f);
5538 }
5539
5540 static FIELD *
5541 make_field(int frow, int fcol, int rows, int cols, bool secure)
5542 {
5543     FIELD *f = new_field(rows, cols, frow, fcol, 0, secure ? 1 : 0);
5544
5545     if (f) {
5546         set_field_back(f, A_UNDERLINE);
5547         set_field_userptr(f, (void *) 0);
5548     }
5549     return (f);
5550 }
5551
5552 static void
5553 display_form(FORM * f)
5554 {
5555     WINDOW *w;
5556     int rows, cols;
5557
5558     scale_form(f, &rows, &cols);
5559
5560     if ((w = newwin(rows + 2, cols + 4, 0, 0)) != (WINDOW *) 0) {
5561         set_form_win(f, w);
5562         set_form_sub(f, derwin(w, rows, cols, 1, 2));
5563         box(w, 0, 0);
5564         keypad(w, TRUE);
5565     }
5566
5567     if (post_form(f) != E_OK)
5568         wrefresh(w);
5569 }
5570
5571 static void
5572 erase_form(FORM * f)
5573 {
5574     WINDOW *w = form_win(f);
5575     WINDOW *s = form_sub(f);
5576
5577     unpost_form(f);
5578     werase(w);
5579     wrefresh(w);
5580     delwin(s);
5581     delwin(w);
5582 }
5583
5584 static int
5585 edit_secure(FIELD * me, int c)
5586 {
5587     int rows, cols, frow, fcol, nrow, nbuf;
5588
5589     if (field_info(me, &rows, &cols, &frow, &fcol, &nrow, &nbuf) == E_OK
5590         && nbuf > 0) {
5591         char *source = field_buffer(me, 1);
5592         char temp[80];
5593         long len;
5594
5595         strcpy(temp, source ? source : "");
5596         len = (long) (char *) field_userptr(me);
5597         if (c <= KEY_MAX) {
5598             if (isgraph(c) && (len + 1) < (int) sizeof(temp)) {
5599                 temp[len++] = (char) c;
5600                 temp[len] = 0;
5601                 set_field_buffer(me, 1, temp);
5602                 c = '*';
5603             } else {
5604                 c = 0;
5605             }
5606         } else {
5607             switch (c) {
5608             case REQ_BEG_FIELD:
5609             case REQ_CLR_EOF:
5610             case REQ_CLR_EOL:
5611             case REQ_DEL_LINE:
5612             case REQ_DEL_WORD:
5613             case REQ_DOWN_CHAR:
5614             case REQ_END_FIELD:
5615             case REQ_INS_CHAR:
5616             case REQ_INS_LINE:
5617             case REQ_LEFT_CHAR:
5618             case REQ_NEW_LINE:
5619             case REQ_NEXT_WORD:
5620             case REQ_PREV_WORD:
5621             case REQ_RIGHT_CHAR:
5622             case REQ_UP_CHAR:
5623                 c = 0;          /* we don't want to do inline editing */
5624                 break;
5625             case REQ_CLR_FIELD:
5626                 if (len) {
5627                     temp[0] = 0;
5628                     set_field_buffer(me, 1, temp);
5629                 }
5630                 break;
5631             case REQ_DEL_CHAR:
5632             case REQ_DEL_PREV:
5633                 if (len) {
5634                     temp[--len] = 0;
5635                     set_field_buffer(me, 1, temp);
5636                 }
5637                 break;
5638             }
5639         }
5640         set_field_userptr(me, (void *) len);
5641     }
5642     return c;
5643 }
5644
5645 static int
5646 form_virtualize(FORM * f, WINDOW *w)
5647 {
5648     /* *INDENT-OFF* */
5649     static const struct {
5650         int code;
5651         int result;
5652     } lookup[] = {
5653         { CTRL('A'),    REQ_NEXT_CHOICE },
5654         { CTRL('B'),    REQ_PREV_WORD },
5655         { CTRL('C'),    REQ_CLR_EOL },
5656         { CTRL('D'),    REQ_DOWN_FIELD },
5657         { CTRL('E'),    REQ_END_FIELD },
5658         { CTRL('F'),    REQ_NEXT_PAGE },
5659         { CTRL('G'),    REQ_DEL_WORD },
5660         { CTRL('H'),    REQ_DEL_PREV },
5661         { CTRL('I'),    REQ_INS_CHAR },
5662         { CTRL('K'),    REQ_CLR_EOF },
5663         { CTRL('L'),    REQ_LEFT_FIELD },
5664         { CTRL('M'),    REQ_NEW_LINE },
5665         { CTRL('N'),    REQ_NEXT_FIELD },
5666         { CTRL('O'),    REQ_INS_LINE },
5667         { CTRL('P'),    REQ_PREV_FIELD },
5668         { CTRL('R'),    REQ_RIGHT_FIELD },
5669         { CTRL('S'),    REQ_BEG_FIELD },
5670         { CTRL('U'),    REQ_UP_FIELD },
5671         { CTRL('V'),    REQ_DEL_CHAR },
5672         { CTRL('W'),    REQ_NEXT_WORD },
5673         { CTRL('X'),    REQ_CLR_FIELD },
5674         { CTRL('Y'),    REQ_DEL_LINE },
5675         { CTRL('Z'),    REQ_PREV_CHOICE },
5676         { ESCAPE,       MAX_FORM_COMMAND + 1 },
5677         { KEY_BACKSPACE, REQ_DEL_PREV },
5678         { KEY_DOWN,     REQ_DOWN_CHAR },
5679         { KEY_END,      REQ_LAST_FIELD },
5680         { KEY_HOME,     REQ_FIRST_FIELD },
5681         { KEY_LEFT,     REQ_LEFT_CHAR },
5682         { KEY_LL,       REQ_LAST_FIELD },
5683         { KEY_NEXT,     REQ_NEXT_FIELD },
5684         { KEY_NPAGE,    REQ_NEXT_PAGE },
5685         { KEY_PPAGE,    REQ_PREV_PAGE },
5686         { KEY_PREVIOUS, REQ_PREV_FIELD },
5687         { KEY_RIGHT,    REQ_RIGHT_CHAR },
5688         { KEY_UP,       REQ_UP_CHAR },
5689         { QUIT,         MAX_FORM_COMMAND + 1 }
5690     };
5691     /* *INDENT-ON* */
5692
5693     static int mode = REQ_INS_MODE;
5694     int c = wGetchar(w);
5695     unsigned n;
5696     FIELD *me = current_field(f);
5697     bool current = TRUE;
5698
5699     if (c == CTRL(']')) {
5700         if (mode == REQ_INS_MODE) {
5701             mode = REQ_OVL_MODE;
5702         } else {
5703             mode = REQ_INS_MODE;
5704         }
5705         c = mode;
5706     } else {
5707         for (n = 0; n < SIZEOF(lookup); n++) {
5708             if (lookup[n].code == c) {
5709                 c = lookup[n].result;
5710                 break;
5711             }
5712         }
5713     }
5714     mvprintw(0, COLS - 6, "(%s)", mode == REQ_INS_MODE ? "INS" : "OVL");
5715
5716     /*
5717      * Force the field that the user is typing into to be in reverse video,
5718      * while the other fields are shown underlined.
5719      */
5720     switch (c) {
5721     case REQ_BEG_FIELD:
5722     case REQ_CLR_EOF:
5723     case REQ_CLR_EOL:
5724     case REQ_CLR_FIELD:
5725     case REQ_DEL_CHAR:
5726     case REQ_DEL_LINE:
5727     case REQ_DEL_PREV:
5728     case REQ_DEL_WORD:
5729     case REQ_END_FIELD:
5730     case REQ_INS_CHAR:
5731     case REQ_INS_LINE:
5732     case REQ_LEFT_CHAR:
5733     case REQ_LEFT_FIELD:
5734     case REQ_NEXT_WORD:
5735     case REQ_RIGHT_CHAR:
5736         current = TRUE;
5737         break;
5738     default:
5739         current = (c < KEY_MAX);
5740         break;
5741     }
5742     if (current) {
5743         c = edit_secure(me, c);
5744         set_field_back(me, A_REVERSE);
5745     } else {
5746         c = edit_secure(me, c);
5747         set_field_back(me, A_UNDERLINE);
5748     }
5749     return c;
5750 }
5751
5752 static int
5753 my_form_driver(FORM * form, int c)
5754 {
5755     if (c == (MAX_FORM_COMMAND + 1)
5756         && form_driver(form, REQ_VALIDATION) == E_OK)
5757         return (TRUE);
5758     else {
5759         beep();
5760         return (FALSE);
5761     }
5762 }
5763
5764 #ifdef NCURSES_VERSION
5765 #define FIELDCHECK_CB(func) bool func(FIELD * fld, const void * data GCC_UNUSED)
5766 #define CHAR_CHECK_CB(func) bool func(int ch, const void *data GCC_UNUSED)
5767 #else
5768 #define FIELDCHECK_CB(func) int func(FIELD * fld, char * data GCC_UNUSED)
5769 #define CHAR_CHECK_CB(func) int func(int ch, char *data GCC_UNUSED)
5770 #endif
5771
5772 /*
5773  * Allow a middle initial, optionally with a '.' to end it.
5774  */
5775 static
5776 FIELDCHECK_CB(mi_field_check)
5777 {
5778     char *s = field_buffer(fld, 0);
5779     int state = 0;
5780     int n;
5781
5782     for (n = 0; s[n] != '\0'; ++n) {
5783         switch (state) {
5784         case 0:
5785             if (s[n] == '.') {
5786                 if (n != 1)
5787                     return FALSE;
5788                 state = 2;
5789             } else if (isspace(UChar(s[n]))) {
5790                 state = 2;
5791             }
5792             break;
5793         case 2:
5794             if (!isspace(UChar(s[n])))
5795                 return FALSE;
5796             break;
5797         }
5798     }
5799
5800     /* force the form to display a leading capital */
5801     if (islower(UChar(s[0]))) {
5802         s[0] = (char) toupper(UChar(s[0]));
5803         set_field_buffer(fld, 0, s);
5804     }
5805     return TRUE;
5806 }
5807
5808 static
5809 CHAR_CHECK_CB(mi_char_check)
5810 {
5811     return ((isalpha(ch) || ch == '.') ? TRUE : FALSE);
5812 }
5813
5814 /*
5815  * Passwords should be at least 6 characters.
5816  */
5817 static
5818 FIELDCHECK_CB(pw_field_check)
5819 {
5820     char *s = field_buffer(fld, 0);
5821     int n;
5822
5823     for (n = 0; s[n] != '\0'; ++n) {
5824         if (isspace(UChar(s[n]))) {
5825             if (n < 6)
5826                 return FALSE;
5827         }
5828     }
5829     return TRUE;
5830 }
5831
5832 static
5833 CHAR_CHECK_CB(pw_char_check)
5834 {
5835     return (isgraph(ch) ? TRUE : FALSE);
5836 }
5837
5838 static void
5839 demo_forms(void)
5840 {
5841     WINDOW *w;
5842     FORM *form;
5843     FIELD *f[12], *secure;
5844     FIELDTYPE *fty_middle = new_fieldtype(mi_field_check, mi_char_check);
5845     FIELDTYPE *fty_passwd = new_fieldtype(pw_field_check, pw_char_check);
5846     int finished = 0, c;
5847     unsigned n = 0;
5848
5849 #ifdef NCURSES_MOUSE_VERSION
5850     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
5851 #endif
5852
5853     move(18, 0);
5854     addstr("Defined edit/traversal keys:   ^Q/ESC- exit form\n");
5855     addstr("^N   -- go to next field       ^P  -- go to previous field\n");
5856     addstr("Home -- go to first field      End -- go to last field\n");
5857     addstr("^L   -- go to field to left    ^R  -- go to field to right\n");
5858     addstr("^U   -- move upward to field   ^D  -- move downward to field\n");
5859     addstr("^W   -- go to next word        ^B  -- go to previous word\n");
5860     addstr("^S   -- go to start of field   ^E  -- go to end of field\n");
5861     addstr("^H   -- delete previous char   ^Y  -- delete line\n");
5862     addstr("^G   -- delete current word    ^C  -- clear to end of line\n");
5863     addstr("^K   -- clear to end of field  ^X  -- clear field\n");
5864     addstr("Arrow keys move within a field as you would expect. ^] toggles overlay mode.");
5865
5866     mvaddstr(4, 57, "Forms Entry Test");
5867
5868     refresh();
5869
5870     /* describe the form */
5871     memset(f, 0, sizeof(f));
5872     f[n++] = make_label(0, 15, "Sample Form");
5873
5874     f[n++] = make_label(2, 0, "Last Name");
5875     f[n++] = make_field(3, 0, 1, 18, FALSE);
5876     set_field_type(f[n - 1], TYPE_ALPHA, 1);
5877
5878     f[n++] = make_label(2, 20, "First Name");
5879     f[n++] = make_field(3, 20, 1, 12, FALSE);
5880     set_field_type(f[n - 1], TYPE_ALPHA, 1);
5881
5882     f[n++] = make_label(2, 34, "Middle Name");
5883     f[n++] = make_field(3, 34, 1, 12, FALSE);
5884     set_field_type(f[n - 1], fty_middle);
5885
5886     f[n++] = make_label(5, 0, "Comments");
5887     f[n++] = make_field(6, 0, 4, 46, FALSE);
5888
5889     f[n++] = make_label(5, 20, "Password:");
5890     secure =
5891         f[n++] = make_field(5, 30, 1, 9, TRUE);
5892     set_field_type(f[n - 1], fty_passwd);
5893     f[n++] = (FIELD *) 0;
5894
5895     if ((form = new_form(f)) != 0) {
5896
5897         display_form(form);
5898
5899         w = form_win(form);
5900         raw();
5901         nonl();                 /* lets us read ^M's */
5902         while (!finished) {
5903             switch (form_driver(form, c = form_virtualize(form, w))) {
5904             case E_OK:
5905                 mvaddstr(5, 57, field_buffer(secure, 1));
5906                 clrtoeol();
5907                 refresh();
5908                 break;
5909             case E_UNKNOWN_COMMAND:
5910                 finished = my_form_driver(form, c);
5911                 break;
5912             default:
5913                 beep();
5914                 break;
5915             }
5916         }
5917
5918         erase_form(form);
5919
5920         free_form(form);
5921     }
5922     for (c = 0; f[c] != 0; c++)
5923         free_field(f[c]);
5924     free_fieldtype(fty_middle);
5925     free_fieldtype(fty_passwd);
5926     noraw();
5927     nl();
5928
5929 #ifdef NCURSES_MOUSE_VERSION
5930     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
5931 #endif
5932 }
5933 #endif /* USE_LIBFORM */
5934
5935 /****************************************************************************
5936  *
5937  * Overlap test
5938  *
5939  ****************************************************************************/
5940
5941 static void
5942 fillwin(WINDOW *win, char ch)
5943 {
5944     int y, x;
5945     int y1, x1;
5946
5947     getmaxyx(win, y1, x1);
5948     for (y = 0; y < y1; y++) {
5949         wmove(win, y, 0);
5950         for (x = 0; x < x1; x++)
5951             waddch(win, UChar(ch));
5952     }
5953 }
5954
5955 static void
5956 crosswin(WINDOW *win, char ch)
5957 {
5958     int y, x;
5959     int y1, x1;
5960
5961     getmaxyx(win, y1, x1);
5962     for (y = 0; y < y1; y++) {
5963         for (x = 0; x < x1; x++)
5964             if (((x > (x1 - 1) / 3) && (x <= (2 * (x1 - 1)) / 3))
5965                 || (((y > (y1 - 1) / 3) && (y <= (2 * (y1 - 1)) / 3)))) {
5966                 wmove(win, y, x);
5967                 waddch(win, UChar(ch));
5968             }
5969     }
5970 }
5971
5972 #define OVERLAP_FLAVORS 5
5973
5974 static void
5975 overlap_helpitem(int state, int item, char *message)
5976 {
5977     int row = (item / 2);
5978     int col = ((item % 2) ? COLS / 2 : 0);
5979
5980     move(LINES - 6 + row, col);
5981     printw("%c%c = %s", state == row ? '>' : ' ', 'a' + item, message);
5982     clrtoeol();
5983 }
5984
5985 static void
5986 overlap_test_1_attr(WINDOW *win, int flavor, int col)
5987 {
5988     short cpair = (short) (1 + (flavor * 2) + col);
5989
5990     switch (flavor) {
5991     case 0:
5992         (void) wattrset(win, A_NORMAL);
5993         break;
5994     case 1:
5995         (void) wattrset(win, A_BOLD);
5996         break;
5997     case 2:
5998         init_pair(cpair, COLOR_BLUE, COLOR_WHITE);
5999         (void) wattrset(win, COLOR_PAIR(cpair) | A_NORMAL);
6000         break;
6001     case 3:
6002         init_pair(cpair, COLOR_WHITE, COLOR_BLUE);
6003         (void) wattrset(win, COLOR_PAIR(cpair) | A_BOLD);
6004         break;
6005     }
6006 }
6007
6008 static void
6009 overlap_test_2_attr(WINDOW *win, int flavor, int col)
6010 {
6011     short cpair = (short) (9 + (flavor * 2) + col);
6012
6013     switch (flavor) {
6014     case 0:
6015         /* no effect */
6016         break;
6017     case 1:
6018         /* no effect */
6019         break;
6020     case 2:
6021         init_pair(cpair, COLOR_RED, COLOR_GREEN);
6022         wbkgdset(win, colored_chtype(' ', A_BLINK, cpair));
6023         break;
6024     case 3:
6025         wbkgdset(win, ' ' | A_NORMAL);
6026         break;
6027     }
6028 }
6029
6030 static int
6031 overlap_help(int state, int flavors[OVERLAP_FLAVORS])
6032 {
6033     int row;
6034     int col;
6035     int item;
6036     const char *ths, *tht;
6037     char msg[80];
6038
6039     if (state < 0)
6040         state += OVERLAP_FLAVORS;
6041     state = state % OVERLAP_FLAVORS;
6042     assert(state >= 0 && state < OVERLAP_FLAVORS);
6043
6044     for (item = 0; item < (2 * OVERLAP_FLAVORS); ++item) {
6045         row = item / 2;
6046         col = item % 2;
6047         ths = col ? "B" : "A";
6048         tht = col ? "A" : "B";
6049
6050         switch (row) {
6051         case 0:
6052             flavors[row] = 0;
6053             sprintf(msg, "refresh %s, then %s, then doupdate.", ths, tht);
6054             break;
6055         case 1:
6056             if (use_colors) {
6057                 flavors[row] %= 4;
6058             } else {
6059                 flavors[row] %= 2;
6060             }
6061             overlap_test_1_attr(stdscr, flavors[row], col);
6062             sprintf(msg, "fill window %s with letter %s.", ths, ths);
6063             break;
6064         case 2:
6065             if (use_colors) {
6066                 flavors[row] %= 4;
6067             } else {
6068                 flavors[row] %= 2;
6069             }
6070             switch (flavors[row]) {
6071             case 0:
6072                 sprintf(msg, "cross pattern in window %s.", ths);
6073                 break;
6074             case 1:
6075                 sprintf(msg, "draw box in window %s.", ths);
6076                 break;
6077             case 2:
6078                 sprintf(msg, "set background of window %s.", ths);
6079                 break;
6080             case 3:
6081                 sprintf(msg, "reset background of window %s.", ths);
6082                 break;
6083             }
6084             break;
6085         case 3:
6086             flavors[row] = 0;
6087             sprintf(msg, "clear window %s.", ths);
6088             break;
6089         case 4:
6090             flavors[row] %= 4;
6091             switch (flavors[row]) {
6092             case 0:
6093                 sprintf(msg, "overwrite %s onto %s.", ths, tht);
6094                 break;
6095             case 1:
6096                 sprintf(msg, "copywin(FALSE) %s onto %s.", ths, tht);
6097                 break;
6098             case 2:
6099                 sprintf(msg, "copywin(TRUE) %s onto %s.", ths, tht);
6100                 break;
6101             case 3:
6102                 sprintf(msg, "overlay %s onto %s.", ths, tht);
6103                 break;
6104             }
6105             break;
6106         }
6107         overlap_helpitem(state, item, msg);
6108         (void) wattrset(stdscr, A_NORMAL);
6109         wbkgdset(stdscr, ' ' | A_NORMAL);
6110     }
6111     move(LINES - 1, 0);
6112     printw("^Q/ESC = terminate test.  Up/down/space select test variations (%d %d).",
6113            state, flavors[state]);
6114
6115     return state;
6116 }
6117
6118 static void
6119 overlap_test_0(WINDOW *a, WINDOW *b)
6120 {
6121     touchwin(a);
6122     touchwin(b);
6123     wnoutrefresh(a);
6124     wnoutrefresh(b);
6125     doupdate();
6126 }
6127
6128 static void
6129 overlap_test_1(int flavor, int col, WINDOW *a, char fill)
6130 {
6131     overlap_test_1_attr(a, flavor, col);
6132     fillwin(a, fill);
6133     (void) wattrset(a, A_NORMAL);
6134 }
6135
6136 static void
6137 overlap_test_2(int flavor, int col, WINDOW *a, char fill)
6138 {
6139     overlap_test_2_attr(a, flavor, col);
6140     switch (flavor) {
6141     case 0:
6142         crosswin(a, fill);
6143         break;
6144     case 1:
6145         box(a, 0, 0);
6146         break;
6147     case 2:
6148         /* done in overlap_test_2_attr */
6149         break;
6150     case 3:
6151         /* done in overlap_test_2_attr */
6152         break;
6153     }
6154 }
6155
6156 static void
6157 overlap_test_3(WINDOW *a)
6158 {
6159     wclear(a);
6160     wmove(a, 0, 0);
6161 }
6162
6163 static void
6164 overlap_test_4(int flavor, WINDOW *a, WINDOW *b)
6165 {
6166     switch (flavor) {
6167     case 0:
6168         overwrite(a, b);
6169         break;
6170     case 1:
6171         copywin(a, b, 0, 0, 0, 0, getmaxy(b), getmaxx(b), FALSE);
6172         break;
6173     case 2:
6174         copywin(a, b, 0, 0, 0, 0, getmaxy(b), getmaxx(b), TRUE);
6175         break;
6176     case 3:
6177         overlay(a, b);
6178         break;
6179     }
6180 }
6181
6182 /* test effects of overlapping windows */
6183 static void
6184 overlap_test(void)
6185 {
6186     int ch;
6187     int state, flavor[OVERLAP_FLAVORS];
6188
6189     WINDOW *win1 = newwin(9, 20, 3, 3);
6190     WINDOW *win2 = newwin(9, 20, 9, 16);
6191
6192     curs_set(0);
6193     raw();
6194     refresh();
6195     move(0, 0);
6196     printw("This test shows the behavior of wnoutrefresh() with respect to\n");
6197     printw("the shared region of two overlapping windows A and B.  The cross\n");
6198     printw("pattern in each window does not overlap the other.\n");
6199
6200     memset(flavor, 0, sizeof(flavor));
6201     state = overlap_help(0, flavor);
6202
6203     while (!isQuit(ch = Getchar()))
6204         switch (ch) {
6205         case 'a':               /* refresh window A first, then B */
6206             overlap_test_0(win1, win2);
6207             break;
6208
6209         case 'b':               /* refresh window B first, then A */
6210             overlap_test_0(win2, win1);
6211             break;
6212
6213         case 'c':               /* fill window A so it's visible */
6214             overlap_test_1(flavor[1], 0, win1, 'A');
6215             break;
6216
6217         case 'd':               /* fill window B so it's visible */
6218             overlap_test_1(flavor[1], 1, win2, 'B');
6219             break;
6220
6221         case 'e':               /* cross test pattern in window A */
6222             overlap_test_2(flavor[2], 0, win1, 'A');
6223             break;
6224
6225         case 'f':               /* cross test pattern in window A */
6226             overlap_test_2(flavor[2], 1, win2, 'B');
6227             break;
6228
6229         case 'g':               /* clear window A */
6230             overlap_test_3(win1);
6231             break;
6232
6233         case 'h':               /* clear window B */
6234             overlap_test_3(win2);
6235             break;
6236
6237         case 'i':               /* overwrite A onto B */
6238             overlap_test_4(flavor[4], win1, win2);
6239             break;
6240
6241         case 'j':               /* overwrite B onto A */
6242             overlap_test_4(flavor[4], win2, win1);
6243             break;
6244
6245         case CTRL('n'):
6246         case KEY_DOWN:
6247             state = overlap_help(state + 1, flavor);
6248             break;
6249
6250         case CTRL('p'):
6251         case KEY_UP:
6252             state = overlap_help(state - 1, flavor);
6253             break;
6254
6255         case ' ':
6256             flavor[state] += 1;
6257             state = overlap_help(state, flavor);
6258             break;
6259
6260         case '?':
6261             state = overlap_help(state, flavor);
6262             break;
6263
6264         default:
6265             beep();
6266             break;
6267         }
6268
6269     delwin(win2);
6270     delwin(win1);
6271     erase();
6272     curs_set(1);
6273     endwin();
6274 }
6275
6276 /****************************************************************************
6277  *
6278  * Main sequence
6279  *
6280  ****************************************************************************/
6281
6282 static bool
6283 do_single_test(const char c)
6284 /* perform a single specified test */
6285 {
6286     switch (c) {
6287     case 'a':
6288         getch_test();
6289         break;
6290
6291 #if USE_WIDEC_SUPPORT
6292     case 'A':
6293         get_wch_test();
6294         break;
6295 #endif
6296
6297     case 'b':
6298         attr_test();
6299         break;
6300
6301 #if USE_WIDEC_SUPPORT
6302     case 'B':
6303         wide_attr_test();
6304         break;
6305 #endif
6306
6307     case 'c':
6308         if (!use_colors)
6309             Cannot("does not support color.");
6310         else
6311             color_test();
6312         break;
6313
6314 #if USE_WIDEC_SUPPORT
6315     case 'C':
6316         if (!use_colors)
6317             Cannot("does not support color.");
6318         else
6319             wide_color_test();
6320         break;
6321 #endif
6322
6323     case 'd':
6324         if (!use_colors)
6325             Cannot("does not support color.");
6326         else if (!can_change_color())
6327             Cannot("has hardwired color values.");
6328         else
6329             color_edit();
6330         break;
6331
6332 #if USE_SOFTKEYS
6333     case 'e':
6334         slk_test();
6335         break;
6336 #endif
6337
6338 #if USE_WIDEC_SUPPORT
6339     case 'E':
6340         wide_slk_test();
6341         break;
6342 #endif
6343     case 'f':
6344         acs_display();
6345         break;
6346
6347 #if USE_WIDEC_SUPPORT
6348     case 'F':
6349         wide_acs_display();
6350         break;
6351 #endif
6352
6353 #if USE_LIBPANEL
6354     case 'o':
6355         demo_panels(init_panel, fill_panel);
6356         break;
6357 #endif
6358
6359 #if USE_WIDEC_SUPPORT && USE_LIBPANEL
6360     case 'O':
6361         demo_panels(init_wide_panel, fill_wide_panel);
6362         break;
6363 #endif
6364
6365     case 'g':
6366         acs_and_scroll();
6367         break;
6368
6369     case 'i':
6370         flushinp_test(stdscr);
6371         break;
6372
6373     case 'k':
6374         test_sgr_attributes();
6375         break;
6376
6377 #if USE_LIBMENU
6378     case 'm':
6379         menu_test();
6380         break;
6381 #endif
6382
6383     case 'p':
6384         demo_pad();
6385         break;
6386
6387 #if USE_LIBFORM
6388     case 'r':
6389         demo_forms();
6390         break;
6391 #endif
6392
6393     case 's':
6394         overlap_test();
6395         break;
6396
6397 #if USE_LIBMENU && defined(TRACE)
6398     case 't':
6399         trace_set();
6400         break;
6401 #endif
6402
6403     case '?':
6404         break;
6405
6406     default:
6407         return FALSE;
6408     }
6409
6410     return TRUE;
6411 }
6412
6413 static void
6414 usage(void)
6415 {
6416     static const char *const tbl[] =
6417     {
6418         "Usage: ncurses [options]"
6419         ,""
6420         ,"Options:"
6421 #ifdef NCURSES_VERSION
6422         ,"  -a f,b   set default-colors (assumed white-on-black)"
6423         ,"  -d       use default-colors if terminal supports them"
6424 #endif
6425 #if USE_SOFTKEYS
6426         ,"  -e fmt   specify format for soft-keys test (e)"
6427 #endif
6428 #if HAVE_RIPOFFLINE
6429         ,"  -f       rip-off footer line (can repeat)"
6430         ,"  -h       rip-off header line (can repeat)"
6431 #endif
6432         ,"  -m       do not use colors"
6433         ,"  -p file  rgb values to use in 'd' rather than ncurses's builtin"
6434 #if USE_LIBPANEL
6435         ,"  -s msec  specify nominal time for panel-demo (default: 1, to hold)"
6436 #endif
6437 #ifdef TRACE
6438         ,"  -t mask  specify default trace-level (may toggle with ^T)"
6439 #endif
6440     };
6441     size_t n;
6442     for (n = 0; n < SIZEOF(tbl); n++)
6443         fprintf(stderr, "%s\n", tbl[n]);
6444     ExitProgram(EXIT_FAILURE);
6445 }
6446
6447 static void
6448 set_terminal_modes(void)
6449 {
6450     noraw();
6451     cbreak();
6452     noecho();
6453     scrollok(stdscr, TRUE);
6454     idlok(stdscr, TRUE);
6455     keypad(stdscr, TRUE);
6456 }
6457
6458 #ifdef SIGUSR1
6459 static RETSIGTYPE
6460 announce_sig(int sig)
6461 {
6462     (void) fprintf(stderr, "Handled signal %d\r\n", sig);
6463 }
6464 #endif
6465
6466 #if HAVE_RIPOFFLINE
6467 static int
6468 rip_footer(WINDOW *win, int cols)
6469 {
6470     wbkgd(win, A_REVERSE);
6471     werase(win);
6472     wmove(win, 0, 0);
6473     wprintw(win, "footer: window %p, %d columns", (void *) win, cols);
6474     wnoutrefresh(win);
6475     return OK;
6476 }
6477
6478 static int
6479 rip_header(WINDOW *win, int cols)
6480 {
6481     wbkgd(win, A_REVERSE);
6482     werase(win);
6483     wmove(win, 0, 0);
6484     wprintw(win, "header: window %p, %d columns", (void *) win, cols);
6485     wnoutrefresh(win);
6486     return OK;
6487 }
6488 #endif /* HAVE_RIPOFFLINE */
6489
6490 static void
6491 main_menu(bool top)
6492 {
6493     char command;
6494
6495     do {
6496         (void) puts("This is the ncurses main menu");
6497         (void) puts("a = keyboard and mouse input test");
6498 #if USE_WIDEC_SUPPORT
6499         (void) puts("A = wide-character keyboard and mouse input test");
6500 #endif
6501         (void) puts("b = character attribute test");
6502 #if USE_WIDEC_SUPPORT
6503         (void) puts("B = wide-character attribute test");
6504 #endif
6505         (void) puts("c = color test pattern");
6506 #if USE_WIDEC_SUPPORT
6507         (void) puts("C = color test pattern using wide-character calls");
6508 #endif
6509         if (top)
6510             (void) puts("d = edit RGB color values");
6511 #if USE_SOFTKEYS
6512         (void) puts("e = exercise soft keys");
6513 #if USE_WIDEC_SUPPORT
6514         (void) puts("E = exercise soft keys using wide-characters");
6515 #endif
6516 #endif
6517         (void) puts("f = display ACS characters");
6518 #if USE_WIDEC_SUPPORT
6519         (void) puts("F = display Wide-ACS characters");
6520 #endif
6521         (void) puts("g = display windows and scrolling");
6522         (void) puts("i = test of flushinp()");
6523         (void) puts("k = display character attributes");
6524 #if USE_LIBMENU
6525         (void) puts("m = menu code test");
6526 #endif
6527 #if USE_LIBPANEL
6528         (void) puts("o = exercise panels library");
6529 #if USE_WIDEC_SUPPORT
6530         (void) puts("O = exercise panels with wide-characters");
6531 #endif
6532 #endif
6533         (void) puts("p = exercise pad features");
6534         (void) puts("q = quit");
6535 #if USE_LIBFORM
6536         (void) puts("r = exercise forms code");
6537 #endif
6538         (void) puts("s = overlapping-refresh test");
6539 #if USE_LIBMENU && defined(TRACE)
6540         (void) puts("t = set trace level");
6541 #endif
6542         (void) puts("? = repeat this command summary");
6543
6544         (void) fputs("> ", stdout);
6545         (void) fflush(stdout);  /* necessary under SVr4 curses */
6546
6547         /*
6548          * This used to be an 'fgets()' call.  However (on Linux, at least)
6549          * mixing stream I/O and 'read()' (used in the library) causes the
6550          * input stream to be flushed when switching between the two.
6551          */
6552         command = 0;
6553         for (;;) {
6554             char ch = '\0';
6555             if (read(fileno(stdin), &ch, 1) <= 0) {
6556                 if (command == 0)
6557                     command = 'q';
6558                 break;
6559             } else if (command == 0 && !isspace(UChar(ch))) {
6560                 command = ch;
6561             } else if (ch == '\n' || ch == '\r') {
6562                 if ((command == 'd') && !top) {
6563                     (void) fputs("Do not nest test-d\n", stdout);
6564                     command = 0;
6565                 }
6566                 if (command != 0)
6567                     break;
6568                 (void) fputs("> ", stdout);
6569                 (void) fflush(stdout);
6570             }
6571         }
6572
6573         if (do_single_test(command)) {
6574             /*
6575              * This may be overkill; it's intended to reset everything back
6576              * to the initial terminal modes so that tests don't get in
6577              * each other's way.
6578              */
6579             flushinp();
6580             set_terminal_modes();
6581             reset_prog_mode();
6582             clear();
6583             refresh();
6584             endwin();
6585             if (command == '?') {
6586                 (void) puts("This is the ncurses capability tester.");
6587                 (void)
6588                     puts("You may select a test from the main menu by typing the");
6589                 (void)
6590                     puts("key letter of the choice (the letter to left of the =)");
6591                 (void)
6592                     puts("at the > prompt.  Type `q' to exit.");
6593             }
6594             continue;
6595         }
6596     } while
6597         (command != 'q');
6598 }
6599
6600 /*+-------------------------------------------------------------------------
6601         main(argc,argv)
6602 --------------------------------------------------------------------------*/
6603
6604 #define okCOLOR(n) ((n) >= 0 && (n) < max_colors)
6605 #define okRGB(n)   ((n) >= 0 && (n) <= 1000)
6606
6607 int
6608 main(int argc, char *argv[])
6609 {
6610     int c;
6611     int my_e_param = 1;
6612 #ifdef NCURSES_VERSION
6613     int default_fg = COLOR_WHITE;
6614     int default_bg = COLOR_BLACK;
6615     bool assumed_colors = FALSE;
6616     bool default_colors = FALSE;
6617 #endif
6618     char *palette_file = 0;
6619     bool monochrome = FALSE;
6620
6621     setlocale(LC_ALL, "");
6622
6623     while ((c = getopt(argc, argv, "a:de:fhmp:s:t:")) != -1) {
6624         switch (c) {
6625 #ifdef NCURSES_VERSION
6626         case 'a':
6627             assumed_colors = TRUE;
6628             sscanf(optarg, "%d,%d", &default_fg, &default_bg);
6629             break;
6630         case 'd':
6631             default_colors = TRUE;
6632             break;
6633 #endif
6634         case 'e':
6635             my_e_param = atoi(optarg);
6636 #ifdef NCURSES_VERSION
6637             if (my_e_param > 3) /* allow extended layouts */
6638                 usage();
6639 #else
6640             if (my_e_param > 1)
6641                 usage();
6642 #endif
6643             break;
6644 #if HAVE_RIPOFFLINE
6645         case 'f':
6646             ripoffline(-1, rip_footer);
6647             break;
6648         case 'h':
6649             ripoffline(1, rip_header);
6650             break;
6651 #endif /* HAVE_RIPOFFLINE */
6652         case 'm':
6653             monochrome = TRUE;
6654             break;
6655         case 'p':
6656             palette_file = optarg;
6657             break;
6658 #if USE_LIBPANEL
6659         case 's':
6660             nap_msec = atol(optarg);
6661             break;
6662 #endif
6663 #ifdef TRACE
6664         case 't':
6665             save_trace = (unsigned) strtol(optarg, 0, 0);
6666             break;
6667 #endif
6668         default:
6669             usage();
6670         }
6671     }
6672
6673     /*
6674      * If there's no menus (unlikely for ncurses!), then we'll have to set
6675      * tracing on initially, just in case the user wants to test something that
6676      * doesn't involve wGetchar.
6677      */
6678 #ifdef TRACE
6679     /* enable debugging */
6680 #if !USE_LIBMENU
6681     trace(save_trace);
6682 #else
6683     if (!isatty(fileno(stdin)))
6684         trace(save_trace);
6685 #endif /* USE_LIBMENU */
6686 #endif /* TRACE */
6687
6688 #if USE_SOFTKEYS
6689     /* tell it we're going to play with soft keys */
6690     slk_init(my_e_param);
6691 #endif
6692
6693 #ifdef SIGUSR1
6694     /* set up null signal catcher so we can see what interrupts to getch do */
6695     signal(SIGUSR1, announce_sig);
6696 #endif
6697
6698     /* we must initialize the curses data structure only once */
6699     initscr();
6700     bkgdset(BLANK);
6701
6702     /* tests, in general, will want these modes */
6703     use_colors = monochrome ? FALSE : has_colors();
6704
6705     if (use_colors) {
6706         start_color();
6707 #ifdef NCURSES_VERSION_PATCH
6708         max_colors = COLORS;    /* was > 16 ? 16 : COLORS */
6709 #if HAVE_USE_DEFAULT_COLORS
6710         if (default_colors) {
6711             use_default_colors();
6712             min_colors = -1;
6713         }
6714 #if NCURSES_VERSION_PATCH >= 20000708
6715         if (assumed_colors)
6716             assume_default_colors(default_fg, default_bg);
6717 #endif
6718 #endif
6719 #else /* normal SVr4 curses */
6720         max_colors = COLORS;    /* was > 8 ? 8 : COLORS */
6721 #endif
6722         max_pairs = COLOR_PAIRS;        /* was > 256 ? 256 : COLOR_PAIRS */
6723
6724         if (can_change_color()) {
6725             short cp;
6726             all_colors = typeMalloc(RGB_DATA, (unsigned) max_colors);
6727             for (cp = 0; cp < max_colors; ++cp) {
6728                 color_content(cp,
6729                               &all_colors[cp].red,
6730                               &all_colors[cp].green,
6731                               &all_colors[cp].blue);
6732             }
6733             if (palette_file != 0) {
6734                 FILE *fp = fopen(palette_file, "r");
6735                 if (fp != 0) {
6736                     char buffer[BUFSIZ];
6737                     int red, green, blue;
6738                     int scale = 1000;
6739                     while (fgets(buffer, sizeof(buffer), fp) != 0) {
6740                         if (sscanf(buffer, "scale:%d", &c) == 1) {
6741                             scale = c;
6742                         } else if (sscanf(buffer, "%d:%d %d %d",
6743                                           &c,
6744                                           &red,
6745                                           &green,
6746                                           &blue) == 4
6747                                    && okCOLOR(c)
6748                                    && okRGB(red)
6749                                    && okRGB(green)
6750                                    && okRGB(blue)) {
6751                             all_colors[c].red = (short) ((red * 1000) / scale);
6752                             all_colors[c].green = (short) ((green * 1000) / scale);
6753                             all_colors[c].blue = (short) ((blue * 1000) / scale);
6754                         }
6755                     }
6756                     fclose(fp);
6757                 }
6758             }
6759         }
6760     }
6761     set_terminal_modes();
6762     def_prog_mode();
6763
6764     /*
6765      * Return to terminal mode, so we're guaranteed of being able to
6766      * select terminal commands even if the capabilities are wrong.
6767      */
6768     endwin();
6769
6770 #if HAVE_CURSES_VERSION
6771     (void) printf("Welcome to %s.  Press ? for help.\n", curses_version());
6772 #elif defined(NCURSES_VERSION_MAJOR) && defined(NCURSES_VERSION_MINOR) && defined(NCURSES_VERSION_PATCH)
6773     (void) printf("Welcome to ncurses %d.%d.%d.  Press ? for help.\n",
6774                   NCURSES_VERSION_MAJOR,
6775                   NCURSES_VERSION_MINOR,
6776                   NCURSES_VERSION_PATCH);
6777 #else
6778     (void) puts("Welcome to ncurses.  Press ? for help.");
6779 #endif
6780
6781     main_menu(TRUE);
6782
6783     ExitProgram(EXIT_SUCCESS);
6784 }
6785
6786 /* ncurses.c ends here */