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