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