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