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