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