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