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