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