]> ncurses.scripts.mit.edu Git - ncurses.git/blob - test/ncurses.c
ncurses 6.1 - patch 20180721
[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.507 2018/06/23 21:35:06 tom Exp $
44
45 ***************************************************************************/
46
47 #include <test.priv.h>
48
49 #ifdef __hpux
50 #undef mvwdelch                 /* HPUX 11.23 macro will not compile */
51 #endif
52
53 #if HAVE_GETTIMEOFDAY
54 #if HAVE_SYS_TIME_H && HAVE_SYS_TIME_SELECT
55 #include <sys/time.h>
56 #endif
57 #if HAVE_SYS_SELECT_H
58 #include <sys/select.h>
59 #endif
60 #endif
61
62 #if USE_LIBPANEL
63 #include <panel.h>
64 #endif
65
66 #if USE_LIBMENU
67 #include <menu.h>
68 #endif
69
70 #if USE_LIBFORM
71 #include <form.h>
72 #endif
73
74 #ifdef NCURSES_VERSION
75
76 #define NCURSES_CONST_PARAM const void
77
78 #ifdef TRACE
79 static unsigned save_trace = TRACE_ORDINARY | TRACE_ICALLS | TRACE_CALLS;
80 #endif
81
82 #else
83
84 #define NCURSES_CONST_PARAM char
85
86 #define mmask_t chtype          /* not specified in XSI */
87
88 #ifndef ACS_S3
89 #ifdef CURSES_ACS_ARRAY
90 #define ACS_S3          (CURSES_ACS_ARRAY['p'])         /* scan line 3 */
91 #define ACS_S7          (CURSES_ACS_ARRAY['r'])         /* scan line 7 */
92 #define ACS_LEQUAL      (CURSES_ACS_ARRAY['y'])         /* less/equal */
93 #define ACS_GEQUAL      (CURSES_ACS_ARRAY['z'])         /* greater/equal */
94 #define ACS_PI          (CURSES_ACS_ARRAY['{'])         /* Pi */
95 #define ACS_NEQUAL      (CURSES_ACS_ARRAY['|'])         /* not equal */
96 #define ACS_STERLING    (CURSES_ACS_ARRAY['}'])         /* UK pound sign */
97 #else
98 #define ACS_S3          (A_ALTCHARSET + 'p')    /* scan line 3 */
99 #define ACS_S7          (A_ALTCHARSET + 'r')    /* scan line 7 */
100 #define ACS_LEQUAL      (A_ALTCHARSET + 'y')    /* less/equal */
101 #define ACS_GEQUAL      (A_ALTCHARSET + 'z')    /* greater/equal */
102 #define ACS_PI          (A_ALTCHARSET + '{')    /* Pi */
103 #define ACS_NEQUAL      (A_ALTCHARSET + '|')    /* not equal */
104 #define ACS_STERLING    (A_ALTCHARSET + '}')    /* UK pound sign */
105 #endif
106 #endif /* ACS_S3 */
107
108 #ifndef WACS_S3
109 #ifdef CURSES_WACS_ARRAY
110 #define WACS_S3         (&(CURSES_WACS_ARRAY['p']))     /* scan line 3 */
111 #define WACS_S7         (&(CURSES_WACS_ARRAY['r']))     /* scan line 7 */
112 #define WACS_LEQUAL     (&(CURSES_WACS_ARRAY['y']))     /* less/equal */
113 #define WACS_GEQUAL     (&(CURSES_WACS_ARRAY['z']))     /* greater/equal */
114 #define WACS_PI         (&(CURSES_WACS_ARRAY['{']))     /* Pi */
115 #define WACS_NEQUAL     (&(CURSES_WACS_ARRAY['|']))     /* not equal */
116 #define WACS_STERLING   (&(CURSES_WACS_ARRAY['}']))     /* UK pound sign */
117 #endif
118 #endif
119
120 #endif
121
122 #if HAVE_WCSRTOMBS
123 #define count_wchars(src, len, state)      wcsrtombs(0,   &src, len, state)
124 #define trans_wchars(dst, src, len, state) wcsrtombs(dst, &src, len, state)
125 #define reset_wchars(state) init_mb(state)
126 #elif HAVE_WCSTOMBS && HAVE_MBTOWC && HAVE_MBLEN
127 #define count_wchars(src, len, state)      wcstombs(0,   src, len)
128 #define trans_wchars(dst, src, len, state) wcstombs(dst, src, len)
129 #define reset_wchars(state) IGNORE_RC(mblen(NULL, 0)), IGNORE_RC(mbtowc(NULL, NULL, 0))
130 #define state_unused
131 #endif
132
133 #if HAVE_MBSRTOWCS
134 #define count_mbytes(src, len, state)      mbsrtowcs(0,   &src, len, state)
135 #define trans_mbytes(dst, src, len, state) mbsrtowcs(dst, &src, len, state)
136 #define reset_mbytes(state) init_mb(state)
137 #elif HAVE_MBSTOWCS && HAVE_MBTOWC && HAVE_MBLEN
138 #define count_mbytes(src, len, state)      mbstowcs(0,   src, len)
139 #define trans_mbytes(dst, src, len, state) mbstowcs(dst, src, len)
140 #define reset_mbytes(state) IGNORE_RC(mblen(NULL, 0)), IGNORE_RC(mbtowc(NULL, NULL, 0))
141 #define state_unused
142 #endif
143
144 #define ToggleAcs(temp,real) temp = ((temp == real) ? NULL : real)
145
146 #define P(string)       printw("%s\n", string)
147
148 #define BLANK           ' '     /* this is the background character */
149
150 static int MaxColors;           /* the actual number of colors we'll use */
151 static int MinColors;           /* the minimum color code */
152 static bool UseColors;          /* true if we use colors */
153
154 #undef max_pairs
155 static int max_pairs;           /* ...and the number of color pairs */
156
157 #if HAVE_COLOR_CONTENT
158 typedef struct {
159     NCURSES_COLOR_T red;
160     NCURSES_COLOR_T green;
161     NCURSES_COLOR_T blue;
162 } RGB_DATA;
163
164 static RGB_DATA *all_colors;
165 #endif
166
167 static void main_menu(bool);
168 static void failed(const char *s) GCC_NORETURN;
169
170 static void
171 failed(const char *s)
172 {
173     perror(s);
174     endwin();
175     ExitProgram(EXIT_FAILURE);
176 }
177
178 static void
179 Repaint(void)
180 {
181     touchwin(stdscr);
182 #if HAVE_CURSCR
183     touchwin(curscr);
184     wrefresh(curscr);
185 #else
186     wrefresh(stdscr);
187 #endif
188 }
189
190 static bool
191 isQuit(int c, bool escape)
192 {
193     return ((c) == QUIT || (escape && ((c) == ESCAPE)));
194 }
195 #define case_QUIT       QUIT: case ESCAPE
196
197 /* Common function to allow ^T to toggle trace-mode in the middle of a test
198  * so that trace-files can be made smaller.
199  */
200 static int
201 wGetchar(WINDOW *win)
202 {
203     int c;
204 #ifdef TRACE
205     while ((c = wgetch(win)) == CTRL('T')) {
206         if (_nc_tracing) {
207             save_trace = _nc_tracing;
208             Trace(("TOGGLE-TRACING OFF"));
209             _nc_tracing = 0;
210         } else {
211             _nc_tracing = save_trace;
212         }
213         trace(_nc_tracing);
214         if (_nc_tracing)
215             Trace(("TOGGLE-TRACING ON"));
216     }
217 #else
218     c = wgetch(win);
219 #endif
220     return c;
221 }
222 #define Getchar() wGetchar(stdscr)
223
224 #if USE_SOFTKEYS
225 /* replaces wgetnstr(), since we want to be able to edit values */
226 static void
227 wGetstring(WINDOW *win, char *buffer, int limit)
228 {
229     int y0, x0, x, ch;
230     bool done = FALSE;
231
232     echo();
233     getyx(win, y0, x0);
234     (void) wattrset(win, A_REVERSE);
235
236     x = (int) strlen(buffer);
237     while (!done) {
238         if (x > (int) strlen(buffer))
239             x = (int) strlen(buffer);
240         wmove(win, y0, x0);
241         wprintw(win, "%-*s", limit, buffer);
242         wmove(win, y0, x0 + x);
243         switch (ch = wGetchar(win)) {
244         case '\n':
245         case KEY_ENTER:
246             done = TRUE;
247             break;
248         case CTRL('U'):
249             *buffer = '\0';
250             break;
251         case '\b':
252         case KEY_BACKSPACE:
253         case KEY_DC:
254             if (x > 0) {
255                 int j;
256                 for (j = --x; (buffer[j] = buffer[j + 1]) != '\0'; ++j) {
257                     ;
258                 }
259             } else {
260                 beep();
261             }
262             break;
263         case KEY_LEFT:
264             if (x > 0) {
265                 --x;
266             } else {
267                 flash();
268             }
269             break;
270         case KEY_RIGHT:
271             ++x;
272             break;
273         default:
274             if (!isprint(ch) || ch >= KEY_MIN) {
275                 beep();
276             } else if ((int) strlen(buffer) < limit) {
277                 int j;
278                 for (j = (int) strlen(buffer) + 1; j > x; --j) {
279                     buffer[j] = buffer[j - 1];
280                 }
281                 buffer[x++] = (char) ch;
282             } else {
283                 flash();
284             }
285         }
286     }
287
288     wattroff(win, A_REVERSE);
289     wmove(win, y0, x0);
290     noecho();
291 }
292 #endif
293
294 #if USE_WIDEC_SUPPORT
295 static wchar_t
296 fullwidth_digit(int ch)
297 {
298     return (wchar_t) (ch + 0xff10 - '0');
299 }
300
301 static void
302 make_fullwidth_text(wchar_t *target, const char *source)
303 {
304     int ch;
305     while ((ch = *source++) != 0) {
306         *target++ = fullwidth_digit(ch);
307     }
308     *target = 0;
309 }
310
311 static void
312 make_narrow_text(wchar_t *target, const char *source)
313 {
314     int ch;
315     while ((ch = *source++) != 0) {
316         *target++ = (wchar_t) ch;
317     }
318     *target = 0;
319 }
320
321 #if USE_LIBPANEL
322 static void
323 make_fullwidth_digit(cchar_t *target, int digit)
324 {
325     wchar_t source[2];
326
327     source[0] = fullwidth_digit(digit + '0');
328     source[1] = 0;
329     setcchar(target, source, A_NORMAL, 0, 0);
330 }
331 #endif
332
333 static int
334 wGet_wchar(WINDOW *win, wint_t *result)
335 {
336     int c;
337 #ifdef TRACE
338     while ((c = wget_wch(win, result)) == CTRL('T')) {
339         if (_nc_tracing) {
340             save_trace = _nc_tracing;
341             Trace(("TOGGLE-TRACING OFF"));
342             _nc_tracing = 0;
343         } else {
344             _nc_tracing = save_trace;
345         }
346         trace(_nc_tracing);
347         if (_nc_tracing)
348             Trace(("TOGGLE-TRACING ON"));
349     }
350 #else
351     c = wget_wch(win, result);
352 #endif
353     return c;
354 }
355 #define Get_wchar(result) wGet_wchar(stdscr, result)
356
357 /* replaces wgetn_wstr(), since we want to be able to edit values */
358 #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
4371     do {
4372         switch (c) {
4373         case CTRL('L'):
4374             Repaint();
4375             break;
4376         case 'a':
4377             ToggleAcs(last_show_wacs, show_wacs_chars);
4378             break;
4379 #ifdef WACS_D_PLUS
4380         case 'd':
4381             ToggleAcs(last_show_wacs, show_wacs_chars_double);
4382             break;
4383 #endif
4384 #ifdef WACS_T_PLUS
4385         case 't':
4386             ToggleAcs(last_show_wacs, show_wacs_chars_thick);
4387             break;
4388 #endif
4389         case 'w':
4390             if (pagesize == 32) {
4391                 pagesize = 256;
4392             } else {
4393                 pagesize = 32;
4394             }
4395             break;
4396         case 'x':
4397             ToggleAcs(last_show_wacs, show_wbox_chars);
4398             break;
4399         case 'u':
4400             ToggleAcs(last_show_wacs, show_utf8_chars);
4401             break;
4402         default:
4403             if (c < 256 && isdigit(c)) {
4404                 digit = (c - '0');
4405                 last_show_wacs = 0;
4406             } else if (c == '+') {
4407                 ++digit;
4408                 last_show_wacs = 0;
4409             } else if (c == '-' && digit > 0) {
4410                 --digit;
4411                 last_show_wacs = 0;
4412             } else if (c == '>' && repeat < (COLS / 4)) {
4413                 ++repeat;
4414             } else if (c == '<' && repeat > 1) {
4415                 --repeat;
4416             } else if (c == '_') {
4417                 space = (space == ' ') ? '_' : ' ';
4418                 last_show_wacs = 0;
4419             } else if (cycle_w_attr(c, &at_code, &attr, my_list, my_size)
4420                        || cycle_colors(c, &fg, &bg, &pair)) {
4421                 if (last_show_wacs != 0)
4422                     break;
4423             } else {
4424                 beep();
4425                 break;
4426             }
4427             break;
4428         }
4429         if (pagesize != 32) {
4430             show_paged_widechars(digit, pagesize, repeat, space, attr, pair);
4431         } else if (last_show_wacs != 0) {
4432             last_show_wacs(repeat, attr, pair);
4433         } else {
4434             show_upper_widechars(digit * 32 + 128, repeat, space, attr, pair);
4435         }
4436
4437         MvPrintw(LINES - 4, 0,
4438                  "Select: a/d/t WACS, w=all x=box, u UTF-8, ^L repaint");
4439         MvPrintw(LINES - 3, 2,
4440                  "0-9,+/- non-ASCII, </> repeat, _ space, ESC=quit");
4441         if (UseColors) {
4442             MvPrintw(LINES - 2, 2,
4443                      "v/V, f/F, b/B cycle through video attributes (%s) and color %d/%d.",
4444                      my_list[at_code].name,
4445                      fg, bg);
4446         } else {
4447             MvPrintw(LINES - 2, 2,
4448                      "v/V cycles through video attributes (%s).",
4449                      my_list[at_code].name);
4450         }
4451         refresh();
4452     } while (!isQuit(c = Getchar(), TRUE));
4453
4454     Pause();
4455     erase();
4456     endwin();
4457     return OK;
4458 }
4459
4460 #endif
4461
4462 /*
4463  * Graphic-rendition test (adapted from vttest)
4464  */
4465 static int
4466 sgr_attr_test(bool recur GCC_UNUSED)
4467 {
4468     int pass;
4469
4470     for (pass = 0; pass < 2; pass++) {
4471         chtype normal = ((pass == 0 ? A_NORMAL : A_REVERSE)) | BLANK;
4472
4473         /* Use non-default colors if possible to exercise bce a little */
4474         if (UseColors) {
4475             init_pair(1, COLOR_WHITE, COLOR_BLUE);
4476             normal |= (chtype) COLOR_PAIR(1);
4477         }
4478         bkgdset(normal);
4479         erase();
4480         MvPrintw(1, 20, "Graphic rendition test pattern:");
4481
4482         MvPrintw(4, 1, "vanilla");
4483
4484 #define set_sgr(mask) bkgdset((normal^(mask)));
4485         set_sgr(A_BOLD);
4486         MvPrintw(4, 40, "bold");
4487
4488         set_sgr(A_UNDERLINE);
4489         MvPrintw(6, 6, "underline");
4490
4491         set_sgr(A_BOLD | A_UNDERLINE);
4492         MvPrintw(6, 45, "bold underline");
4493
4494         set_sgr(A_BLINK);
4495         MvPrintw(8, 1, "blink");
4496
4497         set_sgr(A_BLINK | A_BOLD);
4498         MvPrintw(8, 40, "bold blink");
4499
4500         set_sgr(A_UNDERLINE | A_BLINK);
4501         MvPrintw(10, 6, "underline blink");
4502
4503         set_sgr(A_BOLD | A_UNDERLINE | A_BLINK);
4504         MvPrintw(10, 45, "bold underline blink");
4505
4506         set_sgr(A_REVERSE);
4507         MvPrintw(12, 1, "negative");
4508
4509         set_sgr(A_BOLD | A_REVERSE);
4510         MvPrintw(12, 40, "bold negative");
4511
4512         set_sgr(A_UNDERLINE | A_REVERSE);
4513         MvPrintw(14, 6, "underline negative");
4514
4515         set_sgr(A_BOLD | A_UNDERLINE | A_REVERSE);
4516         MvPrintw(14, 45, "bold underline negative");
4517
4518         set_sgr(A_BLINK | A_REVERSE);
4519         MvPrintw(16, 1, "blink negative");
4520
4521         set_sgr(A_BOLD | A_BLINK | A_REVERSE);
4522         MvPrintw(16, 40, "bold blink negative");
4523
4524         set_sgr(A_UNDERLINE | A_BLINK | A_REVERSE);
4525         MvPrintw(18, 6, "underline blink negative");
4526
4527         set_sgr(A_BOLD | A_UNDERLINE | A_BLINK | A_REVERSE);
4528         MvPrintw(18, 45, "bold underline blink negative");
4529
4530         bkgdset(normal);
4531         MvPrintw(LINES - 2, 1, "%s background. ", pass == 0 ? "Dark" :
4532                  "Light");
4533         clrtoeol();
4534         Pause();
4535     }
4536
4537     bkgdset(A_NORMAL | BLANK);
4538     erase();
4539     endwin();
4540     return OK;
4541 }
4542
4543 /****************************************************************************
4544  *
4545  * Windows and scrolling tester.
4546  *
4547  ****************************************************************************/
4548
4549 #define BOTLINES        4       /* number of line stolen from screen bottom */
4550
4551 typedef struct {
4552     int y, x;
4553 } pair;
4554
4555 #define FRAME struct frame
4556 FRAME
4557 {
4558     FRAME *next, *last;
4559     bool do_scroll;
4560     bool do_keypad;
4561     WINDOW *wind;
4562 };
4563
4564 #if defined(NCURSES_VERSION) && NCURSES_EXT_FUNCS
4565 #if (NCURSES_VERSION_PATCH < 20070331)
4566 #define is_keypad(win)   (win)->_use_keypad
4567 #define is_scrollok(win) (win)->_scroll
4568 #endif
4569 #else
4570 #define is_keypad(win)   FALSE
4571 #define is_scrollok(win) FALSE
4572 #endif
4573
4574 static WINDOW *
4575 frame_win(FRAME * curp)
4576 {
4577     return (curp != 0) ? curp->wind : stdscr;
4578 }
4579
4580 /* We need to know if these flags are actually set, so don't look in FRAME.
4581  * These names are known to work with SVr4 curses as well as ncurses.  The
4582  * _use_keypad name does not work with Solaris 8.
4583  */
4584 static bool
4585 HaveKeypad(FRAME * curp)
4586 {
4587     WINDOW *win = frame_win(curp);
4588     (void) win;
4589     return is_keypad(win);
4590 }
4591
4592 static bool
4593 HaveScroll(FRAME * curp)
4594 {
4595     WINDOW *win = frame_win(curp);
4596     (void) win;
4597     return is_scrollok(win);
4598 }
4599
4600 static void
4601 newwin_legend(FRAME * curp)
4602 {
4603 #define DATA(num, name) { name, num }
4604     static const struct {
4605         const char *msg;
4606         int code;
4607     } legend[] = {
4608         DATA(0, "^C = create window"),
4609             DATA(0, "^N = next window"),
4610             DATA(0, "^P = previous window"),
4611             DATA(0, "^F = scroll forward"),
4612             DATA(0, "^B = scroll backward"),
4613             DATA(1, "^K = keypad(%s)"),
4614             DATA(2, "^S = scrollok(%s)"),
4615             DATA(0, "^W = save window"),
4616             DATA(0, "^R = restore window"),
4617 #if HAVE_WRESIZE
4618             DATA(0, "^X = resize"),
4619 #endif
4620             DATA(3, "^Q%s = exit")
4621     };
4622 #undef DATA
4623     size_t n;
4624     int x;
4625     bool do_keypad = HaveKeypad(curp);
4626     bool do_scroll = HaveScroll(curp);
4627     char buf[BUFSIZ];
4628
4629     move(LINES - 4, 0);
4630     for (n = 0; n < SIZEOF(legend); n++) {
4631         switch (legend[n].code) {
4632         default:
4633             _nc_STRCPY(buf, legend[n].msg, sizeof(buf));
4634             break;
4635         case 1:
4636             _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf))
4637                         legend[n].msg, do_keypad ? "yes" : "no");
4638             break;
4639         case 2:
4640             _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf))
4641                         legend[n].msg, do_scroll ? "yes" : "no");
4642             break;
4643         case 3:
4644             _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf))
4645                         legend[n].msg, do_keypad ? "/ESC" : "");
4646             break;
4647         }
4648         x = getcurx(stdscr);
4649         addstr((COLS < (x + 3 + (int) strlen(buf))) ? "\n" : (n ? ", " : ""));
4650         addstr(buf);
4651     }
4652     clrtoeol();
4653 }
4654
4655 static void
4656 transient(FRAME * curp, NCURSES_CONST char *msg)
4657 {
4658     newwin_legend(curp);
4659     if (msg) {
4660         MvAddStr(LINES - 1, 0, msg);
4661         refresh();
4662         napms(1000);
4663     }
4664
4665     move(LINES - 1, 0);
4666     printw("%s characters are echoed, window should %sscroll.",
4667            HaveKeypad(curp) ? "Non-arrow" : "All other",
4668            HaveScroll(curp) ? "" : "not ");
4669     clrtoeol();
4670 }
4671
4672 static void
4673 newwin_report(FRAME * curp)
4674 /* report on the cursor's current position, then restore it */
4675 {
4676     WINDOW *win = frame_win(curp);
4677     int y, x;
4678
4679     if (win != stdscr)
4680         transient(curp, (char *) 0);
4681     getyx(win, y, x);
4682     move(LINES - 1, COLS - 17);
4683     printw("Y = %2d X = %2d", y, x);
4684     if (win != stdscr)
4685         refresh();
4686     else
4687         wmove(win, y, x);
4688 }
4689
4690 static pair *
4691 selectcell(int uli, int ulj, int lri, int lrj)
4692 /* arrows keys move cursor, return location at current on non-arrow key */
4693 {
4694     static pair res;            /* result cell */
4695     int si = lri - uli + 1;     /* depth of the select area */
4696     int sj = lrj - ulj + 1;     /* width of the select area */
4697     int i = 0, j = 0;           /* offsets into the select area */
4698
4699     res.y = uli;
4700     res.x = ulj;
4701     for (;;) {
4702         move(uli + i, ulj + j);
4703         newwin_report((FRAME *) 0);
4704
4705         switch (Getchar()) {
4706         case KEY_UP:
4707             i += si - 1;
4708             break;
4709         case KEY_DOWN:
4710             i++;
4711             break;
4712         case KEY_LEFT:
4713             j += sj - 1;
4714             break;
4715         case KEY_RIGHT:
4716             j++;
4717             break;
4718         case case_QUIT:
4719             return ((pair *) 0);
4720 #ifdef NCURSES_MOUSE_VERSION
4721         case KEY_MOUSE:
4722             {
4723                 MEVENT event;
4724
4725                 getmouse(&event);
4726                 if (event.y > uli && event.x > ulj) {
4727                     i = event.y - uli;
4728                     j = event.x - ulj;
4729                 } else {
4730                     beep();
4731                     break;
4732                 }
4733             }
4734 #endif
4735             /* FALLTHRU */
4736         default:
4737             res.y = uli + i;
4738             res.x = ulj + j;
4739             return (&res);
4740         }
4741         i %= si;
4742         j %= sj;
4743     }
4744 }
4745
4746 static void
4747 outerbox(pair ul, pair lr, bool onoff)
4748 /* draw or erase a box *outside* the given pair of corners */
4749 {
4750     MvAddCh(ul.y - 1, lr.x - 1, onoff ? ACS_ULCORNER : ' ');
4751     MvAddCh(ul.y - 1, lr.x + 1, onoff ? ACS_URCORNER : ' ');
4752     MvAddCh(lr.y + 1, lr.x + 1, onoff ? ACS_LRCORNER : ' ');
4753     MvAddCh(lr.y + 1, ul.x - 1, onoff ? ACS_LLCORNER : ' ');
4754     move(ul.y - 1, ul.x);
4755     hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
4756     move(ul.y, ul.x - 1);
4757     vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
4758     move(lr.y + 1, ul.x);
4759     hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
4760     move(ul.y, lr.x + 1);
4761     vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
4762 }
4763
4764 static WINDOW *
4765 getwindow(void)
4766 /* Ask user for a window definition */
4767 {
4768     WINDOW *rwindow;
4769     pair ul, lr, *tmp;
4770
4771     move(0, 0);
4772     clrtoeol();
4773     addstr("Use arrows to move cursor, anything else to mark corner 1");
4774     refresh();
4775     if ((tmp = selectcell(2, 1, LINES - BOTLINES - 2, COLS - 2)) == (pair *) 0)
4776         return ((WINDOW *) 0);
4777     memcpy(&ul, tmp, sizeof(pair));
4778     MvAddCh(ul.y - 1, ul.x - 1, ACS_ULCORNER);
4779     move(0, 0);
4780     clrtoeol();
4781     addstr("Use arrows to move cursor, anything else to mark corner 2");
4782     refresh();
4783     if ((tmp = selectcell(ul.y, ul.x, LINES - BOTLINES - 2, COLS - 2)) ==
4784         (pair *) 0)
4785         return ((WINDOW *) 0);
4786     memcpy(&lr, tmp, sizeof(pair));
4787
4788     rwindow = subwin(stdscr, lr.y - ul.y + 1, lr.x - ul.x + 1, ul.y, ul.x);
4789
4790     outerbox(ul, lr, TRUE);
4791     refresh();
4792
4793     if (rwindow != 0)
4794         wrefresh(rwindow);
4795
4796     move(0, 0);
4797     clrtoeol();
4798     return (rwindow);
4799 }
4800
4801 static void
4802 newwin_move(FRAME * curp, int dy, int dx)
4803 {
4804     WINDOW *win = frame_win(curp);
4805     int cur_y, cur_x;
4806     int max_y, max_x;
4807
4808     getyx(win, cur_y, cur_x);
4809     getmaxyx(win, max_y, max_x);
4810     if ((cur_x += dx) < 0)
4811         cur_x = 0;
4812     else if (cur_x >= max_x)
4813         cur_x = max_x - 1;
4814     if ((cur_y += dy) < 0)
4815         cur_y = 0;
4816     else if (cur_y >= max_y)
4817         cur_y = max_y - 1;
4818     wmove(win, cur_y, cur_x);
4819 }
4820
4821 static FRAME *
4822 delete_framed(FRAME * fp, bool showit)
4823 {
4824     FRAME *np = 0;
4825
4826     if (fp != 0) {
4827         fp->last->next = fp->next;
4828         fp->next->last = fp->last;
4829
4830         if (showit) {
4831             werase(fp->wind);
4832             wrefresh(fp->wind);
4833         }
4834         delwin(fp->wind);
4835
4836         np = (fp == fp->next) ? NULL : fp->next;
4837         free(fp);
4838     }
4839     return np;
4840 }
4841
4842 static int
4843 scroll_test(bool recur GCC_UNUSED)
4844 /* Demonstrate windows */
4845 {
4846     int c;
4847     FRAME *current = (FRAME *) 0, *neww;
4848     WINDOW *usescr;
4849 #if HAVE_PUTWIN && HAVE_GETWIN
4850     FILE *fp;
4851 #endif
4852
4853 #define DUMPFILE        "screendump"
4854
4855 #ifdef NCURSES_MOUSE_VERSION
4856     mousemask(BUTTON1_CLICKED, (mmask_t *) 0);
4857 #endif
4858     c = CTRL('C');
4859     raw();
4860     do {
4861         transient((FRAME *) 0, (char *) 0);
4862         switch (c) {
4863         case CTRL('C'):
4864             if ((neww = typeCalloc(FRAME, (size_t) 1)) == 0) {
4865                 failed("scroll_test");
4866                 goto breakout;
4867             }
4868             if ((neww->wind = getwindow()) == (WINDOW *) 0) {
4869                 failed("scroll_test");
4870                 free(neww);
4871                 goto breakout;
4872             }
4873
4874             if (current == 0) { /* First element,  */
4875                 neww->next = neww;      /*   so point it at itself */
4876                 neww->last = neww;
4877             } else {
4878                 neww->next = current->next;
4879                 neww->last = current;
4880                 neww->last->next = neww;
4881                 neww->next->last = neww;
4882             }
4883             current = neww;
4884             /* SVr4 curses sets the keypad on all newly-created windows to
4885              * false.  Someone reported that PDCurses makes new windows inherit
4886              * this flag.  Remove the following 'keypad()' call to test this
4887              */
4888             keypad(current->wind, TRUE);
4889             current->do_keypad = HaveKeypad(current);
4890             current->do_scroll = HaveScroll(current);
4891             break;
4892
4893         case CTRL('N'): /* go to next window */
4894             if (current)
4895                 current = current->next;
4896             break;
4897
4898         case CTRL('P'): /* go to previous window */
4899             if (current)
4900                 current = current->last;
4901             break;
4902
4903         case CTRL('F'): /* scroll current window forward */
4904             if (current)
4905                 wscrl(frame_win(current), 1);
4906             break;
4907
4908         case CTRL('B'): /* scroll current window backwards */
4909             if (current)
4910                 wscrl(frame_win(current), -1);
4911             break;
4912
4913         case CTRL('K'): /* toggle keypad mode for current */
4914             if (current) {
4915                 current->do_keypad = !current->do_keypad;
4916                 keypad(current->wind, current->do_keypad);
4917             }
4918             break;
4919
4920         case CTRL('S'):
4921             if (current) {
4922                 current->do_scroll = !current->do_scroll;
4923                 scrollok(current->wind, current->do_scroll);
4924             }
4925             break;
4926
4927 #if HAVE_PUTWIN && HAVE_GETWIN
4928         case CTRL('W'): /* save and delete window */
4929             if ((current != 0) && (current == current->next)) {
4930                 transient(current, "Will not save/delete ONLY window");
4931                 break;
4932             } else if ((fp = fopen(DUMPFILE, "w")) == (FILE *) 0) {
4933                 transient(current, "Can't open screen dump file");
4934             } else {
4935                 int rc = putwin(frame_win(current), fp);
4936                 (void) fclose(fp);
4937
4938                 if (rc == OK) {
4939                     current = delete_framed(current, TRUE);
4940                 } else {
4941                     transient(current, "Can't write screen dump file");
4942                 }
4943             }
4944             break;
4945
4946         case CTRL('R'): /* restore window */
4947             if ((fp = fopen(DUMPFILE, "r")) == (FILE *) 0) {
4948                 transient(current, "Can't open screen dump file");
4949             } else {
4950                 if ((neww = typeCalloc(FRAME, (size_t) 1)) != 0) {
4951
4952                     neww->next = current ? current->next : 0;
4953                     neww->last = current;
4954                     if (neww->last != 0)
4955                         neww->last->next = neww;
4956                     if (neww->next != 0)
4957                         neww->next->last = neww;
4958
4959                     neww->wind = getwin(fp);
4960
4961                     wrefresh(neww->wind);
4962                 } else {
4963                     failed("scroll_test");
4964                 }
4965                 (void) fclose(fp);
4966             }
4967             break;
4968 #endif
4969
4970 #if HAVE_WRESIZE
4971         case CTRL('X'): /* resize window */
4972             if (current) {
4973                 pair *tmp, ul, lr;
4974                 int i, mx, my;
4975
4976                 move(0, 0);
4977                 clrtoeol();
4978                 addstr("Use arrows to move cursor, anything else to mark new corner");
4979                 refresh();
4980
4981                 getbegyx(current->wind, ul.y, ul.x);
4982
4983                 tmp = selectcell(ul.y, ul.x, LINES - BOTLINES - 2, COLS - 2);
4984                 if (tmp == (pair *) 0) {
4985                     beep();
4986                     break;
4987                 }
4988
4989                 getmaxyx(current->wind, lr.y, lr.x);
4990                 lr.y += (ul.y - 1);
4991                 lr.x += (ul.x - 1);
4992                 outerbox(ul, lr, FALSE);
4993                 wnoutrefresh(stdscr);
4994
4995                 /* strictly cosmetic hack for the test */
4996                 getmaxyx(current->wind, my, mx);
4997                 if (my > tmp->y - ul.y) {
4998                     getyx(current->wind, lr.y, lr.x);
4999                     wmove(current->wind, tmp->y - ul.y + 1, 0);
5000                     wclrtobot(current->wind);
5001                     wmove(current->wind, lr.y, lr.x);
5002                 }
5003                 if (mx > tmp->x - ul.x)
5004                     for (i = 0; i < my; i++) {
5005                         wmove(current->wind, i, tmp->x - ul.x + 1);
5006                         wclrtoeol(current->wind);
5007                     }
5008                 wnoutrefresh(current->wind);
5009
5010                 memcpy(&lr, tmp, sizeof(pair));
5011                 (void) wresize(current->wind, lr.y - ul.y + 0, lr.x - ul.x + 0);
5012
5013                 getbegyx(current->wind, ul.y, ul.x);
5014                 getmaxyx(current->wind, lr.y, lr.x);
5015                 lr.y += (ul.y - 1);
5016                 lr.x += (ul.x - 1);
5017                 outerbox(ul, lr, TRUE);
5018                 wnoutrefresh(stdscr);
5019
5020                 wnoutrefresh(current->wind);
5021                 move(0, 0);
5022                 clrtoeol();
5023                 doupdate();
5024             }
5025             break;
5026 #endif /* HAVE_WRESIZE */
5027
5028         case KEY_UP:
5029             newwin_move(current, -1, 0);
5030             break;
5031         case KEY_DOWN:
5032             newwin_move(current, 1, 0);
5033             break;
5034         case KEY_LEFT:
5035             newwin_move(current, 0, -1);
5036             break;
5037         case KEY_RIGHT:
5038             newwin_move(current, 0, 1);
5039             break;
5040
5041         case KEY_BACKSPACE:
5042             /* FALLTHROUGH */
5043         case KEY_DC:
5044             {
5045                 int y, x;
5046                 getyx(frame_win(current), y, x);
5047                 if (--x < 0) {
5048                     if (--y < 0)
5049                         break;
5050                     x = getmaxx(frame_win(current)) - 1;
5051                 }
5052                 (void) mvwdelch(frame_win(current), y, x);
5053             }
5054             break;
5055
5056         case '\r':
5057             c = '\n';
5058             /* FALLTHROUGH */
5059
5060         default:
5061             if (current)
5062                 waddch(current->wind, (chtype) c);
5063             else
5064                 beep();
5065             break;
5066         }
5067         newwin_report(current);
5068         usescr = frame_win(current);
5069         wrefresh(usescr);
5070     } while
5071         (!isQuit(c = wGetchar(usescr), TRUE)
5072          && (c != ERR));
5073
5074   breakout:
5075     while (current != 0)
5076         current = delete_framed(current, FALSE);
5077
5078     scrollok(stdscr, TRUE);     /* reset to driver's default */
5079 #ifdef NCURSES_MOUSE_VERSION
5080     mousemask(0, (mmask_t *) 0);
5081 #endif
5082     noraw();
5083     erase();
5084     endwin();
5085     return OK;
5086 }
5087
5088 /****************************************************************************
5089  *
5090  * Panels tester
5091  *
5092  ****************************************************************************/
5093
5094 #if USE_LIBPANEL
5095 static int nap_msec = 1;
5096
5097 static NCURSES_CONST char *mod[] =
5098 {
5099     "test ",
5100     "TEST ",
5101     "(**) ",
5102     "*()* ",
5103     "<--> ",
5104     "LAST "
5105 };
5106
5107 /*+-------------------------------------------------------------------------
5108         wait_a_while(msec)
5109 --------------------------------------------------------------------------*/
5110 static void
5111 wait_a_while(int msec GCC_UNUSED)
5112 {
5113 #if HAVE_NAPMS
5114     if (nap_msec == 1)
5115         wGetchar(stdscr);
5116     else
5117         napms(nap_msec);
5118 #else
5119     if (nap_msec == 1)
5120         wGetchar(stdscr);
5121     else if (msec > 1000)
5122         sleep((unsigned) msec / 1000);
5123     else
5124         sleep(1);
5125 #endif
5126 }                               /* end of wait_a_while */
5127
5128 /*+-------------------------------------------------------------------------
5129         saywhat(text)
5130 --------------------------------------------------------------------------*/
5131 static void
5132 saywhat(NCURSES_CONST char *text)
5133 {
5134     wmove(stdscr, LINES - 1, 0);
5135     wclrtoeol(stdscr);
5136     if (text != 0 && *text != '\0') {
5137         waddstr(stdscr, text);
5138         waddstr(stdscr, "; ");
5139     }
5140     waddstr(stdscr, "press any key to continue");
5141 }                               /* end of saywhat */
5142
5143 /*+-------------------------------------------------------------------------
5144         mkpanel(rows,cols,tly,tlx) - alloc a win and panel and associate them
5145 --------------------------------------------------------------------------*/
5146 static PANEL *
5147 mkpanel(NCURSES_COLOR_T color, int rows, int cols, int tly, int tlx)
5148 {
5149     WINDOW *win;
5150     PANEL *pan = 0;
5151
5152     if ((win = newwin(rows, cols, tly, tlx)) != 0) {
5153         if ((pan = new_panel(win)) == 0) {
5154             delwin(win);
5155         } else if (UseColors) {
5156             NCURSES_COLOR_T fg = (NCURSES_COLOR_T) ((color == COLOR_BLUE)
5157                                                     ? COLOR_WHITE
5158                                                     : COLOR_BLACK);
5159             NCURSES_COLOR_T bg = color;
5160
5161             init_pair(color, fg, bg);
5162             wbkgdset(win, (attr_t) (COLOR_PAIR(color) | ' '));
5163         } else {
5164             wbkgdset(win, A_BOLD | ' ');
5165         }
5166     }
5167     return pan;
5168 }                               /* end of mkpanel */
5169
5170 /*+-------------------------------------------------------------------------
5171         rmpanel(pan)
5172 --------------------------------------------------------------------------*/
5173 static void
5174 rmpanel(PANEL *pan)
5175 {
5176     WINDOW *win = panel_window(pan);
5177     del_panel(pan);
5178     delwin(win);
5179 }                               /* end of rmpanel */
5180
5181 /*+-------------------------------------------------------------------------
5182         pflush()
5183 --------------------------------------------------------------------------*/
5184 static void
5185 pflush(void)
5186 {
5187     update_panels();
5188     doupdate();
5189 }                               /* end of pflush */
5190
5191 /*+-------------------------------------------------------------------------
5192         fill_panel(win)
5193 --------------------------------------------------------------------------*/
5194 static void
5195 init_panel(WINDOW *win)
5196 {
5197     register int y, x;
5198
5199     for (y = 0; y < LINES - 1; y++) {
5200         for (x = 0; x < COLS; x++)
5201             wprintw(win, "%d", (y + x) % 10);
5202     }
5203 }
5204
5205 static void
5206 fill_panel(PANEL *pan)
5207 {
5208     WINDOW *win = panel_window(pan);
5209     const char *userptr = (const char *) panel_userptr(pan);
5210     int num = (userptr && *userptr) ? userptr[1] : '?';
5211     int y, x;
5212
5213     wmove(win, 1, 1);
5214     wprintw(win, "-pan%c-", num);
5215     wclrtoeol(win);
5216     box(win, 0, 0);
5217     for (y = 2; y < getmaxy(win) - 1; y++) {
5218         for (x = 1; x < getmaxx(win) - 1; x++) {
5219             wmove(win, y, x);
5220             waddch(win, UChar(num));
5221         }
5222     }
5223 }
5224
5225 #if USE_WIDEC_SUPPORT
5226 static void
5227 init_wide_panel(WINDOW *win)
5228 {
5229     int digit;
5230     cchar_t temp[10];
5231
5232     for (digit = 0; digit < 10; ++digit)
5233         make_fullwidth_digit(&temp[digit], digit);
5234
5235     do {
5236         int y, x;
5237         getyx(stdscr, y, x);
5238         digit = (y + x / 2) % 10;
5239     } while (wadd_wch(win, &temp[digit]) != ERR);
5240 }
5241
5242 static void
5243 fill_wide_panel(PANEL *pan)
5244 {
5245     WINDOW *win = panel_window(pan);
5246     const char *userptr = (const char *) panel_userptr(pan);
5247     int num = (userptr && *userptr) ? userptr[1] : '?';
5248     int y, x;
5249
5250     wmove(win, 1, 1);
5251     wprintw(win, "-pan%c-", num);
5252     wclrtoeol(win);
5253     box(win, 0, 0);
5254     for (y = 2; y < getmaxy(win) - 1; y++) {
5255         for (x = 1; x < getmaxx(win) - 1; x++) {
5256             wmove(win, y, x);
5257             waddch(win, UChar(num));
5258         }
5259     }
5260 }
5261 #endif
5262
5263 #define MAX_PANELS 5
5264
5265 static void
5266 canned_panel(PANEL *px[MAX_PANELS + 1], NCURSES_CONST char *cmd)
5267 {
5268     int which = cmd[1] - '0';
5269
5270     saywhat(cmd);
5271     switch (*cmd) {
5272     case 'h':
5273         hide_panel(px[which]);
5274         break;
5275     case 's':
5276         show_panel(px[which]);
5277         break;
5278     case 't':
5279         top_panel(px[which]);
5280         break;
5281     case 'b':
5282         bottom_panel(px[which]);
5283         break;
5284     case 'd':
5285         rmpanel(px[which]);
5286         break;
5287     }
5288     pflush();
5289     wait_a_while(nap_msec);
5290 }
5291
5292 static int
5293 demo_panels(void (*InitPanel) (WINDOW *), void (*FillPanel) (PANEL *))
5294 {
5295     int count;
5296     int itmp;
5297     PANEL *px[MAX_PANELS + 1];
5298
5299     scrollok(stdscr, FALSE);    /* we don't want stdscr to scroll! */
5300     refresh();
5301
5302     InitPanel(stdscr);
5303     for (count = 0; count < 5; count++) {
5304         px[1] = mkpanel(COLOR_RED,
5305                         LINES / 2 - 2,
5306                         COLS / 8 + 1,
5307                         0,
5308                         0);
5309         set_panel_userptr(px[1], (NCURSES_CONST void *) "p1");
5310
5311         px[2] = mkpanel(COLOR_GREEN,
5312                         LINES / 2 + 1,
5313                         COLS / 7,
5314                         LINES / 4,
5315                         COLS / 10);
5316         set_panel_userptr(px[2], (NCURSES_CONST void *) "p2");
5317
5318         px[3] = mkpanel(COLOR_YELLOW,
5319                         LINES / 4,
5320                         COLS / 10,
5321                         LINES / 2,
5322                         COLS / 9);
5323         set_panel_userptr(px[3], (NCURSES_CONST void *) "p3");
5324
5325         px[4] = mkpanel(COLOR_BLUE,
5326                         LINES / 2 - 2,
5327                         COLS / 8,
5328                         LINES / 2 - 2,
5329                         COLS / 3);
5330         set_panel_userptr(px[4], (NCURSES_CONST void *) "p4");
5331
5332         px[5] = mkpanel(COLOR_MAGENTA,
5333                         LINES / 2 - 2,
5334                         COLS / 8,
5335                         LINES / 2,
5336                         COLS / 2 - 2);
5337         set_panel_userptr(px[5], (NCURSES_CONST void *) "p5");
5338
5339         FillPanel(px[1]);
5340         FillPanel(px[2]);
5341         FillPanel(px[3]);
5342         FillPanel(px[4]);
5343         FillPanel(px[5]);
5344
5345         hide_panel(px[4]);
5346         hide_panel(px[5]);
5347         pflush();
5348         saywhat("");
5349         wait_a_while(nap_msec);
5350
5351         saywhat("h3 s1 s2 s4 s5");
5352         move_panel(px[1], 0, 0);
5353         hide_panel(px[3]);
5354         show_panel(px[1]);
5355         show_panel(px[2]);
5356         show_panel(px[4]);
5357         show_panel(px[5]);
5358         pflush();
5359         wait_a_while(nap_msec);
5360
5361         canned_panel(px, "s1");
5362         canned_panel(px, "s2");
5363
5364         saywhat("m2");
5365         move_panel(px[2], LINES / 3 + 1, COLS / 8);
5366         pflush();
5367         wait_a_while(nap_msec);
5368
5369         canned_panel(px, "s3");
5370
5371         saywhat("m3");
5372         move_panel(px[3], LINES / 4 + 1, COLS / 15);
5373         pflush();
5374         wait_a_while(nap_msec);
5375
5376         canned_panel(px, "b3");
5377         canned_panel(px, "s4");
5378         canned_panel(px, "s5");
5379         canned_panel(px, "t3");
5380         canned_panel(px, "t1");
5381         canned_panel(px, "t2");
5382         canned_panel(px, "t3");
5383         canned_panel(px, "t4");
5384
5385         for (itmp = 0; itmp < 6; itmp++) {
5386             WINDOW *w4 = panel_window(px[4]);
5387             WINDOW *w5 = panel_window(px[5]);
5388
5389             saywhat("m4");
5390             wmove(w4, LINES / 8, 1);
5391             waddstr(w4, mod[itmp]);
5392             move_panel(px[4], LINES / 6, itmp * (COLS / 8));
5393             wmove(w5, LINES / 6, 1);
5394             waddstr(w5, mod[itmp]);
5395             pflush();
5396             wait_a_while(nap_msec);
5397
5398             saywhat("m5");
5399             wmove(w4, LINES / 6, 1);
5400             waddstr(w4, mod[itmp]);
5401             move_panel(px[5], LINES / 3 - 1, (itmp * 10) + 6);
5402             wmove(w5, LINES / 8, 1);
5403             waddstr(w5, mod[itmp]);
5404             pflush();
5405             wait_a_while(nap_msec);
5406         }
5407
5408         saywhat("m4");
5409         move_panel(px[4], LINES / 6, itmp * (COLS / 8));
5410         pflush();
5411         wait_a_while(nap_msec);
5412
5413         canned_panel(px, "t5");
5414         canned_panel(px, "t2");
5415         canned_panel(px, "t1");
5416         canned_panel(px, "d2");
5417         canned_panel(px, "h3");
5418         canned_panel(px, "d1");
5419         canned_panel(px, "d4");
5420         canned_panel(px, "d5");
5421         canned_panel(px, "d3");
5422
5423         wait_a_while(nap_msec);
5424         if (nap_msec == 1)
5425             break;
5426         nap_msec = 100L;
5427     }
5428
5429     erase();
5430     endwin();
5431     return OK;
5432 }
5433
5434 #if USE_LIBPANEL
5435 static int
5436 panel_test(bool recur GCC_UNUSED)
5437 {
5438     return demo_panels(init_panel, fill_panel);
5439 }
5440 #endif
5441
5442 #if USE_WIDEC_SUPPORT && USE_LIBPANEL
5443 static int
5444 x_panel_test(bool recur GCC_UNUSED)
5445 {
5446     return demo_panels(init_wide_panel, fill_wide_panel);
5447 }
5448 #endif
5449 #endif /* USE_LIBPANEL */
5450
5451 /****************************************************************************
5452  *
5453  * Pad tester
5454  *
5455  ****************************************************************************/
5456
5457 #if HAVE_NEWPAD
5458
5459 /* The behavior of mvhline, mvvline for negative/zero length is unspecified,
5460  * though we can rely on negative x/y values to stop the macro.
5461  */
5462 static void
5463 do_h_line(int y, int x, chtype c, int to)
5464 {
5465     if ((to) > (x))
5466         MvHLine(y, x, c, (to) - (x));
5467 }
5468
5469 static void
5470 do_v_line(int y, int x, chtype c, int to)
5471 {
5472     if ((to) > (y))
5473         MvVLine(y, x, c, (to) - (y));
5474 }
5475
5476 #define GRIDSIZE        3
5477
5478 static bool pending_pan = FALSE;
5479 static bool show_panner_legend = TRUE;
5480
5481 static int
5482 panner_legend(int line)
5483 {
5484     static const char *const legend[] =
5485     {
5486         "Use arrow keys (or U,D,L,R) to pan, ESC to quit, ! to shell-out.",
5487         "Use +,- (or j,k) to grow/shrink the panner vertically.",
5488         "Use <,> (or h,l) to grow/shrink the panner horizontally.",
5489         "Number repeats.  Toggle legend:? filler:a timer:t scrollmark:s."
5490     };
5491     int n = ((int) SIZEOF(legend) - (LINES - line));
5492     if (n >= 0) {
5493         if (move(line, 0) != ERR) {
5494             if (show_panner_legend)
5495                 printw("%s", legend[n]);
5496             clrtoeol();
5497             return show_panner_legend;
5498         }
5499     }
5500     return FALSE;
5501 }
5502
5503 static void
5504 panner_h_cleanup(int from_y, int from_x, int to_x)
5505 {
5506     if (!panner_legend(from_y))
5507         do_h_line(from_y, from_x, ' ', to_x);
5508 }
5509
5510 static void
5511 panner_v_cleanup(int from_y, int from_x, int to_y)
5512 {
5513     if (!panner_legend(from_y))
5514         do_v_line(from_y, from_x, ' ', to_y);
5515 }
5516
5517 static void
5518 fill_pad(WINDOW *panpad, bool pan_lines, bool colored)
5519 {
5520     int y, x;
5521     unsigned gridcount = 0;
5522     chtype fill = 0;
5523 #ifdef A_COLOR
5524     if (colored)
5525         fill = (chtype) COLOR_PAIR(1);
5526 #endif
5527
5528     wmove(panpad, 0, 0);
5529     for (y = 0; y < getmaxy(panpad); y++) {
5530         for (x = 0; x < getmaxx(panpad); x++) {
5531             if (y % GRIDSIZE == 0 && x % GRIDSIZE == 0) {
5532                 if (y == 0 && x == 0)
5533                     waddch(panpad, pan_lines ? ACS_ULCORNER : '+');
5534                 else if (y == 0)
5535                     waddch(panpad, pan_lines ? ACS_TTEE : '+');
5536                 else if (y == 0 || x == 0)
5537                     waddch(panpad, pan_lines ? ACS_LTEE : '+');
5538                 else
5539                     waddch(panpad, (chtype) ((pan_lines ? 'a' : 'A') +
5540                                              (int) (gridcount++ % 26)) | fill);
5541             } else if (y % GRIDSIZE == 0)
5542                 waddch(panpad, pan_lines ? ACS_HLINE : '-');
5543             else if (x % GRIDSIZE == 0)
5544                 waddch(panpad, pan_lines ? ACS_VLINE : '|');
5545             else
5546                 waddch(panpad, ' ');
5547         }
5548     }
5549 }
5550
5551 static void
5552 panner(WINDOW *pad,
5553        int top_x, int top_y, int porty, int portx,
5554        int (*pgetc) (WINDOW *),
5555        bool colored)
5556 {
5557 #if HAVE_GETTIMEOFDAY
5558     struct timeval before, after;
5559     bool timing = TRUE;
5560 #endif
5561     bool pan_lines = FALSE;
5562     bool scrollers = TRUE;
5563     int basex = 0;
5564     int basey = 0;
5565     int pxmax, pymax, lowend, highend, c;
5566
5567     getmaxyx(pad, pymax, pxmax);
5568     scrollok(stdscr, FALSE);    /* we don't want stdscr to scroll! */
5569
5570     c = KEY_REFRESH;
5571     do {
5572 #ifdef NCURSES_VERSION
5573         /*
5574          * During shell-out, the user may have resized the window.  Adjust
5575          * the port size of the pad to accommodate this.  Ncurses automatically
5576          * resizes all of the normal windows to fit on the new screen.
5577          */
5578         if (top_x > COLS)
5579             top_x = COLS;
5580         if (portx > COLS)
5581             portx = COLS;
5582         if (top_y > LINES)
5583             top_y = LINES;
5584         if (porty > LINES)
5585             porty = LINES;
5586 #endif
5587         switch (c) {
5588         case KEY_REFRESH:
5589             erase();
5590
5591             /* FALLTHRU */
5592         case HELP_KEY_1:
5593             if (c == HELP_KEY_1)
5594                 show_panner_legend = !show_panner_legend;
5595             panner_legend(LINES - 4);
5596             panner_legend(LINES - 3);
5597             panner_legend(LINES - 2);
5598             panner_legend(LINES - 1);
5599             break;
5600         case 'a':
5601             pan_lines = !pan_lines;
5602             fill_pad(pad, pan_lines, colored);
5603             pending_pan = FALSE;
5604             break;
5605
5606 #if HAVE_GETTIMEOFDAY
5607         case 't':
5608             timing = !timing;
5609             if (!timing)
5610                 panner_legend(LINES - 1);
5611             break;
5612 #endif
5613         case 's':
5614             scrollers = !scrollers;
5615             break;
5616
5617             /* Move the top-left corner of the pad, keeping the bottom-right
5618              * corner fixed.
5619              */
5620         case 'h':               /* increase-columns: move left edge to left */
5621             if (top_x <= 0)
5622                 beep();
5623             else {
5624                 panner_v_cleanup(top_y, top_x, porty);
5625                 top_x--;
5626             }
5627             break;
5628
5629         case 'j':               /* decrease-lines: move top-edge down */
5630             if (top_y >= porty)
5631                 beep();
5632             else {
5633                 panner_h_cleanup(top_y - 1, top_x - (top_x > 0), portx);
5634                 top_y++;
5635             }
5636             break;
5637
5638         case 'k':               /* increase-lines: move top-edge up */
5639             if (top_y <= 0)
5640                 beep();
5641             else {
5642                 top_y--;
5643                 panner_h_cleanup(top_y, top_x, portx);
5644             }
5645             break;
5646
5647         case 'l':               /* decrease-columns: move left-edge to right */
5648             if (top_x >= portx)
5649                 beep();
5650             else {
5651                 panner_v_cleanup(top_y - (top_y > 0), top_x - 1, porty);
5652                 top_x++;
5653             }
5654             break;
5655
5656             /* Move the bottom-right corner of the pad, keeping the top-left
5657              * corner fixed.
5658              */
5659         case KEY_IC:            /* increase-columns: move right-edge to right */
5660             if (portx >= pxmax || portx >= COLS)
5661                 beep();
5662             else {
5663                 panner_v_cleanup(top_y - (top_y > 0), portx - 1, porty);
5664                 ++portx;
5665             }
5666             break;
5667
5668         case KEY_IL:            /* increase-lines: move bottom-edge down */
5669             if (porty >= pymax || porty >= LINES)
5670                 beep();
5671             else {
5672                 panner_h_cleanup(porty - 1, top_x - (top_x > 0), portx);
5673                 ++porty;
5674             }
5675             break;
5676
5677         case KEY_DC:            /* decrease-columns: move bottom edge up */
5678             if (portx <= top_x)
5679                 beep();
5680             else {
5681                 portx--;
5682                 panner_v_cleanup(top_y - (top_y > 0), portx, porty);
5683             }
5684             break;
5685
5686         case KEY_DL:            /* decrease-lines */
5687             if (porty <= top_y)
5688                 beep();
5689             else {
5690                 porty--;
5691                 panner_h_cleanup(porty, top_x - (top_x > 0), portx);
5692             }
5693             break;
5694
5695         case KEY_LEFT:          /* pan leftwards */
5696             if (basex > 0)
5697                 basex--;
5698             else
5699                 beep();
5700             break;
5701
5702         case KEY_RIGHT: /* pan rightwards */
5703             if (basex + portx - (pymax > porty) < pxmax)
5704                 basex++;
5705             else
5706                 beep();
5707             break;
5708
5709         case KEY_UP:            /* pan upwards */
5710             if (basey > 0)
5711                 basey--;
5712             else
5713                 beep();
5714             break;
5715
5716         case KEY_DOWN:          /* pan downwards */
5717             if (basey + porty - (pxmax > portx) < pymax)
5718                 basey++;
5719             else
5720                 beep();
5721             break;
5722
5723         case 'H':
5724         case KEY_HOME:
5725         case KEY_FIND:
5726             basey = 0;
5727             break;
5728
5729         case 'E':
5730         case KEY_END:
5731         case KEY_SELECT:
5732             basey = pymax - porty;
5733             if (basey < 0)
5734                 basey = 0;
5735             break;
5736
5737         default:
5738             beep();
5739             break;
5740         }
5741
5742         MvAddCh(top_y - 1, top_x - 1, ACS_ULCORNER);
5743         do_v_line(top_y, top_x - 1, ACS_VLINE, porty);
5744         do_h_line(top_y - 1, top_x, ACS_HLINE, portx);
5745
5746         if (scrollers && (pxmax > portx - 1)) {
5747             int length = (portx - top_x - 1);
5748             float ratio = ((float) length) / ((float) pxmax);
5749
5750             lowend = (int) ((float) top_x + ((float) basex * ratio));
5751             highend = (int) ((float) top_x + ((float) (basex + length) * ratio));
5752
5753             do_h_line(porty - 1, top_x, ACS_HLINE, lowend);
5754             if (highend < portx) {
5755                 attron(A_REVERSE);
5756                 do_h_line(porty - 1, lowend, ' ', highend + 1);
5757                 attroff(A_REVERSE);
5758                 do_h_line(porty - 1, highend + 1, ACS_HLINE, portx);
5759             }
5760         } else
5761             do_h_line(porty - 1, top_x, ACS_HLINE, portx);
5762
5763         if (scrollers && (pymax > porty - 1)) {
5764             int length = (porty - top_y - 1);
5765             float ratio = ((float) length) / ((float) pymax);
5766
5767             lowend = (int) ((float) top_y + ((float) basey * ratio));
5768             highend = (int) ((float) top_y + ((float) (basey + length) * ratio));
5769
5770             do_v_line(top_y, portx - 1, ACS_VLINE, lowend);
5771             if (highend < porty) {
5772                 attron(A_REVERSE);
5773                 do_v_line(lowend, portx - 1, ' ', highend + 1);
5774                 attroff(A_REVERSE);
5775                 do_v_line(highend + 1, portx - 1, ACS_VLINE, porty);
5776             }
5777         } else
5778             do_v_line(top_y, portx - 1, ACS_VLINE, porty);
5779
5780         MvAddCh(top_y - 1, portx - 1, ACS_URCORNER);
5781         MvAddCh(porty - 1, top_x - 1, ACS_LLCORNER);
5782         MvAddCh(porty - 1, portx - 1, ACS_LRCORNER);
5783
5784         if (!pending_pan) {
5785 #if HAVE_GETTIMEOFDAY
5786             gettimeofday(&before, 0);
5787 #endif
5788             wnoutrefresh(stdscr);
5789
5790             pnoutrefresh(pad,
5791                          basey, basex,
5792                          top_y, top_x,
5793                          porty - (pxmax > portx) - 1,
5794                          portx - (pymax > porty) - 1);
5795
5796             doupdate();
5797 #if HAVE_GETTIMEOFDAY
5798 #define TIMEVAL2S(data) ((double) data.tv_sec + ((double) data.tv_usec / 1.0e6))
5799             if (timing) {
5800                 double elapsed;
5801                 gettimeofday(&after, 0);
5802                 elapsed = (TIMEVAL2S(after) - TIMEVAL2S(before));
5803                 move(LINES - 1, COLS - 12);
5804                 printw("Secs: %2.03f", elapsed);
5805                 refresh();
5806             }
5807 #endif
5808         }
5809
5810     } while
5811         ((c = pgetc(pad)) != KEY_EXIT);
5812
5813     scrollok(stdscr, TRUE);     /* reset to driver's default */
5814 }
5815
5816 static int
5817 padgetch(WINDOW *win)
5818 {
5819     static int count;
5820     static int last;
5821     int c;
5822
5823     if ((pending_pan = (count > 0)) != FALSE) {
5824         count--;
5825         pending_pan = (count != 0);
5826     } else {
5827         for (;;) {
5828             switch (c = wGetchar(win)) {
5829             case '!':
5830                 ShellOut(FALSE);
5831                 /* FALLTHRU */
5832             case CTRL('r'):
5833                 endwin();
5834                 refresh();
5835                 c = KEY_REFRESH;
5836                 break;
5837             case CTRL('l'):
5838                 c = KEY_REFRESH;
5839                 break;
5840             case 'U':
5841                 c = KEY_UP;
5842                 break;
5843             case 'D':
5844                 c = KEY_DOWN;
5845                 break;
5846             case 'R':
5847                 c = KEY_RIGHT;
5848                 break;
5849             case 'L':
5850                 c = KEY_LEFT;
5851                 break;
5852             case '+':
5853                 c = KEY_IL;
5854                 break;
5855             case '-':
5856                 c = KEY_DL;
5857                 break;
5858             case '>':
5859                 c = KEY_IC;
5860                 break;
5861             case '<':
5862                 c = KEY_DC;
5863                 break;
5864             case ERR:           /* FALLTHRU */
5865             case case_QUIT:
5866                 count = 0;
5867                 c = KEY_EXIT;
5868                 break;
5869             default:
5870                 if (c >= '0' && c <= '9') {
5871                     count = count * 10 + (c - '0');
5872                     continue;
5873                 }
5874                 break;
5875             }
5876             last = c;
5877             break;
5878         }
5879         if (count > 0)
5880             count--;
5881     }
5882     return (last);
5883 }
5884
5885 #define PAD_HIGH 200
5886 #define PAD_WIDE 200
5887
5888 static int
5889 pad_test(bool recur GCC_UNUSED)
5890 /* Demonstrate pads. */
5891 {
5892     WINDOW *panpad = newpad(PAD_HIGH, PAD_WIDE);
5893
5894     if (panpad == 0) {
5895         Cannot("cannot create requested pad");
5896         return ERR;
5897     }
5898 #ifdef A_COLOR
5899     if (UseColors) {
5900         init_pair(1, COLOR_BLACK, COLOR_GREEN);
5901         init_pair(2, COLOR_CYAN, COLOR_BLUE);
5902         wbkgd(panpad, (chtype) (COLOR_PAIR(2) | ' '));
5903     }
5904 #endif
5905     fill_pad(panpad, FALSE, TRUE);
5906
5907     panner_legend(LINES - 4);
5908     panner_legend(LINES - 3);
5909     panner_legend(LINES - 2);
5910     panner_legend(LINES - 1);
5911
5912     keypad(panpad, TRUE);
5913
5914     /* Make the pad (initially) narrow enough that a trace file won't wrap.
5915      * We'll still be able to widen it during a test, since that's required
5916      * for testing boundaries.
5917      */
5918     panner(panpad, 2, 2, LINES - 5, COLS - 15, padgetch, TRUE);
5919
5920     delwin(panpad);
5921     endwin();
5922     erase();
5923     return OK;
5924 }
5925 #endif /* HAVE_NEWPAD */
5926
5927 /****************************************************************************
5928  *
5929  * Tests from John Burnell's PDCurses tester
5930  *
5931  ****************************************************************************/
5932
5933 static void
5934 Continue(WINDOW *win)
5935 {
5936     noecho();
5937     wmove(win, 10, 1);
5938     MvWAddStr(win, 10, 1, " Press any key to continue");
5939     wrefresh(win);
5940     wGetchar(win);
5941 }
5942
5943 static int
5944 flushinp_test(bool recur GCC_UNUSED)
5945 /* Input test, adapted from John Burnell's PDCurses tester */
5946 {
5947     WINDOW *win = stdscr;
5948     int w, h, bx, by, sw, sh, i;
5949
5950     WINDOW *subWin;
5951     wclear(win);
5952
5953     getmaxyx(win, h, w);
5954     getbegyx(win, by, bx);
5955     sw = w / 3;
5956     sh = h / 3;
5957     if ((subWin = subwin(win, sh, sw, by + h - sh - 2, bx + w - sw - 2)) == 0)
5958         return ERR;
5959
5960 #ifdef A_COLOR
5961     if (UseColors) {
5962         init_pair(2, COLOR_CYAN, COLOR_BLUE);
5963         wbkgd(subWin, (chtype) (COLOR_PAIR(2) | ' '));
5964     }
5965 #endif
5966     (void) wattrset(subWin, A_BOLD);
5967     box(subWin, ACS_VLINE, ACS_HLINE);
5968     MvWAddStr(subWin, 2, 1, "This is a subwindow");
5969     wrefresh(win);
5970
5971     /*
5972      * This used to set 'nocbreak()'.  However, Alexander Lukyanov says that
5973      * it only happened to "work" on SVr4 because that implementation does not
5974      * emulate nocbreak+noecho mode, whereas ncurses does.  To get the desired
5975      * test behavior, we're using 'cbreak()', which will allow a single
5976      * character to return without needing a newline. - T.Dickey 1997/10/11.
5977      */
5978     cbreak();
5979     MvWAddStr(win, 0, 1, "This is a test of the flushinp() call.");
5980
5981     MvWAddStr(win, 2, 1, "Type random keys for 5 seconds.");
5982     MvWAddStr(win, 3, 1,
5983               "These should be discarded (not echoed) after the subwindow goes away.");
5984     wrefresh(win);
5985
5986     for (i = 0; i < 5; i++) {
5987         MvWPrintw(subWin, 1, 1, "Time = %d", i);
5988         wrefresh(subWin);
5989         napms(1000);
5990         flushinp();
5991     }
5992
5993     delwin(subWin);
5994     werase(win);
5995     flash();
5996     wrefresh(win);
5997     napms(1000);
5998
5999     MvWAddStr(win, 2, 1,
6000               "If you were still typing when the window timer expired,");
6001     MvWAddStr(win, 3, 1,
6002               "or else you typed nothing at all while it was running,");
6003     MvWAddStr(win, 4, 1,
6004               "test was invalid.  You'll see garbage or nothing at all. ");
6005     MvWAddStr(win, 6, 1, "Press a key");
6006     wmove(win, 9, 10);
6007     wrefresh(win);
6008     echo();
6009     wGetchar(win);
6010     flushinp();
6011     MvWAddStr(win, 12, 0,
6012               "If you see any key other than what you typed, flushinp() is broken.");
6013     Continue(win);
6014
6015     wmove(win, 9, 10);
6016     wdelch(win);
6017     wrefresh(win);
6018     wmove(win, 12, 0);
6019     clrtoeol();
6020     waddstr(win,
6021             "What you typed should now have been deleted; if not, wdelch() failed.");
6022     Continue(win);
6023
6024     cbreak();
6025     return OK;
6026 }
6027
6028 /****************************************************************************
6029  *
6030  * Menu test
6031  *
6032  ****************************************************************************/
6033
6034 #if USE_LIBMENU
6035
6036 #define MENU_Y  8
6037 #define MENU_X  8
6038
6039 static int
6040 menu_virtualize(int c)
6041 {
6042     if (c == '\n' || c == KEY_EXIT)
6043         return (MAX_COMMAND + 1);
6044     else if (c == 'u')
6045         return (REQ_SCR_ULINE);
6046     else if (c == 'd')
6047         return (REQ_SCR_DLINE);
6048     else if (c == 'b' || c == KEY_NPAGE)
6049         return (REQ_SCR_UPAGE);
6050     else if (c == 'f' || c == KEY_PPAGE)
6051         return (REQ_SCR_DPAGE);
6052     else if (c == 'n' || c == KEY_DOWN)
6053         return (REQ_NEXT_ITEM);
6054     else if (c == 'p' || c == KEY_UP)
6055         return (REQ_PREV_ITEM);
6056     else if (c == ' ')
6057         return (REQ_TOGGLE_ITEM);
6058     else {
6059         if (c != KEY_MOUSE)
6060             beep();
6061         return (c);
6062     }
6063 }
6064
6065 static CONST_MENUS char *animals[] =
6066 {
6067     "Lions",
6068     "Tigers",
6069     "Bears",
6070     "(Oh my!)",
6071     "Newts",
6072     "Platypi",
6073     "Lemurs",
6074     "(Oh really?!)",
6075     "Leopards",
6076     "Panthers",
6077     "Pumas",
6078     "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
6079     "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs, Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
6080     (char *) 0
6081 };
6082
6083 static int
6084 menu_test(bool recur GCC_UNUSED)
6085 {
6086     MENU *m;
6087     ITEM *items[SIZEOF(animals)];
6088     ITEM **ip = items;
6089     CONST_MENUS char **ap;
6090     int mrows, mcols, c;
6091     WINDOW *menuwin;
6092
6093 #ifdef NCURSES_MOUSE_VERSION
6094     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
6095 #endif
6096     MvAddStr(0, 0, "This is the menu test:");
6097     MvAddStr(2, 0, "  Use up and down arrow to move the select bar.");
6098     MvAddStr(3, 0, "  'n' and 'p' act like arrows.");
6099     MvAddStr(4, 0,
6100              "  'b' and 'f' scroll up/down (page), 'u' and 'd' (line).");
6101     MvAddStr(5, 0, "  Press return to exit.");
6102     refresh();
6103
6104     for (ap = animals; *ap; ap++) {
6105         if ((*ip = new_item(*ap, "")) != 0)
6106             ++ip;
6107     }
6108     *ip = (ITEM *) 0;
6109
6110     m = new_menu(items);
6111
6112     set_menu_format(m, (SIZEOF(animals) + 1) / 2, 1);
6113     scale_menu(m, &mrows, &mcols);
6114
6115     menuwin = newwin(mrows + 2, mcols + 2, MENU_Y, MENU_X);
6116     set_menu_win(m, menuwin);
6117     keypad(menuwin, TRUE);
6118     box(menuwin, 0, 0);
6119
6120     set_menu_sub(m, derwin(menuwin, mrows, mcols, 1, 1));
6121
6122     post_menu(m);
6123
6124     while ((c = menu_driver(m, menu_virtualize(wGetchar(menuwin)))) != E_UNKNOWN_COMMAND) {
6125         if (c == E_NOT_POSTED)
6126             break;
6127         if (c == E_REQUEST_DENIED)
6128             beep();
6129         continue;
6130     }
6131
6132     MvPrintw(LINES - 2, 0,
6133              "You chose: %s\n", item_name(current_item(m)));
6134     (void) addstr("Press any key to continue...");
6135     wGetchar(stdscr);
6136
6137     unpost_menu(m);
6138     delwin(menuwin);
6139
6140     free_menu(m);
6141     for (ip = items; *ip; ip++)
6142         free_item(*ip);
6143 #ifdef NCURSES_MOUSE_VERSION
6144     mousemask(0, (mmask_t *) 0);
6145 #endif
6146     return OK;
6147 }
6148
6149 #ifdef TRACE
6150 #define T_TBL(name) { #name, name }
6151 static struct {
6152     const char *name;
6153     unsigned mask;
6154 } t_tbl[] = {
6155
6156     T_TBL(TRACE_DISABLE),
6157         T_TBL(TRACE_TIMES),
6158         T_TBL(TRACE_TPUTS),
6159         T_TBL(TRACE_UPDATE),
6160         T_TBL(TRACE_MOVE),
6161         T_TBL(TRACE_CHARPUT),
6162         T_TBL(TRACE_ORDINARY),
6163         T_TBL(TRACE_CALLS),
6164         T_TBL(TRACE_VIRTPUT),
6165         T_TBL(TRACE_IEVENT),
6166         T_TBL(TRACE_BITS),
6167         T_TBL(TRACE_ICALLS),
6168         T_TBL(TRACE_CCALLS),
6169         T_TBL(TRACE_DATABASE),
6170         T_TBL(TRACE_ATTRS),
6171         T_TBL(TRACE_MAXIMUM),
6172     {
6173         (char *) 0, 0
6174     }
6175 };
6176
6177 static char *
6178 tracetrace(unsigned tlevel)
6179 {
6180     static char *buf;
6181     static size_t need = 12;
6182     int n;
6183
6184     if (buf == 0) {
6185         for (n = 0; t_tbl[n].name != 0; n++)
6186             need += strlen(t_tbl[n].name) + 2;
6187         buf = typeMalloc(char, need);
6188         if (!buf)
6189             failed("tracetrace");
6190     }
6191     _nc_SPRINTF(buf, _nc_SLIMIT(need) "0x%02x = {", tlevel);
6192     if (tlevel == 0) {
6193         _nc_STRCAT(buf, t_tbl[0].name, need);
6194         _nc_STRCAT(buf, ", ", need);
6195     } else {
6196         for (n = 1; t_tbl[n].name != 0; n++)
6197             if ((tlevel & t_tbl[n].mask) == t_tbl[n].mask) {
6198                 _nc_STRCAT(buf, t_tbl[n].name, need);
6199                 _nc_STRCAT(buf, ", ", need);
6200             }
6201     }
6202     if (buf[strlen(buf) - 2] == ',')
6203         buf[strlen(buf) - 2] = '\0';
6204     _nc_STRCAT(buf, "}", need);
6205     return buf;
6206 }
6207
6208 /* fake a dynamically reconfigurable menu using the 0th entry to deselect
6209  * the others
6210  */
6211 static int
6212 run_trace_menu(MENU * m)
6213 {
6214     ITEM **items;
6215     ITEM *i, **p;
6216
6217     for (;;) {
6218         bool changed = FALSE;
6219         switch (menu_driver(m, menu_virtualize(wGetchar(menu_win(m))))) {
6220         case E_UNKNOWN_COMMAND:
6221             return FALSE;
6222         default:
6223             items = menu_items(m);
6224             i = current_item(m);
6225             if (i == items[0]) {
6226                 if (item_value(i)) {
6227                     for (p = items + 1; *p != 0; p++)
6228                         if (item_value(*p)) {
6229                             set_item_value(*p, FALSE);
6230                             changed = TRUE;
6231                         }
6232                 }
6233             } else {
6234                 for (p = items + 1; *p != 0; p++)
6235                     if (item_value(*p)) {
6236                         set_item_value(items[0], FALSE);
6237                         changed = TRUE;
6238                         break;
6239                     }
6240             }
6241             if (!changed)
6242                 return TRUE;
6243         }
6244     }
6245 }
6246
6247 static int
6248 trace_set(bool recur GCC_UNUSED)
6249 /* interactively set the trace level */
6250 {
6251     MENU *m;
6252     ITEM *items[SIZEOF(t_tbl)];
6253     ITEM **ip = items;
6254     int mrows, mcols;
6255     unsigned newtrace;
6256     int n;
6257     WINDOW *menuwin;
6258
6259     MvAddStr(0, 0, "Interactively set trace level:");
6260     MvAddStr(2, 0, "  Press space bar to toggle a selection.");
6261     MvAddStr(3, 0, "  Use up and down arrow to move the select bar.");
6262     MvAddStr(4, 0, "  Press return to set the trace level.");
6263     MvPrintw(6, 0, "(Current trace level is %s)", tracetrace(_nc_tracing));
6264
6265     refresh();
6266
6267     for (n = 0; t_tbl[n].name != 0; n++) {
6268         if ((*ip = new_item(t_tbl[n].name, "")) != 0) {
6269             ++ip;
6270         }
6271     }
6272     *ip = (ITEM *) 0;
6273
6274     m = new_menu(items);
6275
6276     set_menu_format(m, 0, 2);
6277     scale_menu(m, &mrows, &mcols);
6278
6279     menu_opts_off(m, O_ONEVALUE);
6280     menuwin = newwin(mrows + 2, mcols + 2, MENU_Y, MENU_X);
6281     set_menu_win(m, menuwin);
6282     keypad(menuwin, TRUE);
6283     box(menuwin, 0, 0);
6284
6285     set_menu_sub(m, derwin(menuwin, mrows, mcols, 1, 1));
6286
6287     post_menu(m);
6288
6289     for (ip = menu_items(m); *ip; ip++) {
6290         unsigned mask = t_tbl[item_index(*ip)].mask;
6291         if (mask == 0)
6292             set_item_value(*ip, _nc_tracing == 0);
6293         else if ((mask & _nc_tracing) == mask)
6294             set_item_value(*ip, TRUE);
6295     }
6296
6297     while (run_trace_menu(m))
6298         continue;
6299
6300     newtrace = 0;
6301     for (ip = menu_items(m); *ip; ip++)
6302         if (item_value(*ip))
6303             newtrace |= t_tbl[item_index(*ip)].mask;
6304     trace(newtrace);
6305     Trace(("trace level interactively set to %s", tracetrace(_nc_tracing)));
6306
6307     MvPrintw(LINES - 2, 0,
6308              "Trace level is %s\n", tracetrace(_nc_tracing));
6309     (void) addstr("Press any key to continue...");
6310     wGetchar(stdscr);
6311
6312     unpost_menu(m);
6313     delwin(menuwin);
6314
6315     free_menu(m);
6316     for (ip = items; *ip; ip++)
6317         free_item(*ip);
6318
6319     return OK;
6320 }
6321 #endif /* TRACE */
6322 #endif /* USE_LIBMENU */
6323
6324 /****************************************************************************
6325  *
6326  * Forms test
6327  *
6328  ****************************************************************************/
6329 #if USE_LIBFORM
6330 static FIELD *
6331 make_label(int frow, int fcol, NCURSES_CONST char *label)
6332 {
6333     FIELD *f = new_field(1, (int) strlen(label), frow, fcol, 0, 0);
6334
6335     if (f) {
6336         set_field_buffer(f, 0, label);
6337         set_field_opts(f, (int) ((unsigned) field_opts(f) & ~O_ACTIVE));
6338     }
6339     return (f);
6340 }
6341
6342 static FIELD *
6343 make_field(int frow, int fcol, int rows, int cols, bool secure)
6344 {
6345     FIELD *f = new_field(rows, cols, frow, fcol, 0, secure ? 1 : 0);
6346
6347     if (f) {
6348         set_field_back(f, A_UNDERLINE);
6349         set_field_userptr(f, (void *) 0);
6350     }
6351     return (f);
6352 }
6353
6354 static void
6355 display_form(FORM *f)
6356 {
6357     WINDOW *w;
6358     int rows, cols;
6359
6360     scale_form(f, &rows, &cols);
6361
6362     if ((w = newwin(rows + 2, cols + 4, 0, 0)) != (WINDOW *) 0) {
6363         set_form_win(f, w);
6364         set_form_sub(f, derwin(w, rows, cols, 1, 2));
6365         box(w, 0, 0);
6366         keypad(w, TRUE);
6367         if (post_form(f) != E_OK)
6368             wrefresh(w);
6369     }
6370 }
6371
6372 static void
6373 erase_form(FORM *f)
6374 {
6375     WINDOW *w = form_win(f);
6376     WINDOW *s = form_sub(f);
6377
6378     unpost_form(f);
6379     werase(w);
6380     wrefresh(w);
6381     delwin(s);
6382     delwin(w);
6383 }
6384
6385 static int
6386 edit_secure(FIELD *me, int c)
6387 {
6388     int rows, cols, frow, fcol, nrow, nbuf;
6389
6390     if (field_info(me, &rows, &cols, &frow, &fcol, &nrow, &nbuf) == E_OK
6391         && nbuf > 0) {
6392         char *source = field_buffer(me, 1);
6393         size_t have = (source ? strlen(source) : 0) + 1;
6394         size_t need = 80 + have;
6395         char *temp = malloc(need);
6396         size_t len;
6397
6398         if (temp != 0) {
6399             _nc_STRNCPY(temp, source ? source : "", have + 1);
6400             len = (size_t) (char *) field_userptr(me);
6401             if (c <= KEY_MAX) {
6402                 if (isgraph(c) && (len + 1) < sizeof(temp)) {
6403                     temp[len++] = (char) c;
6404                     temp[len] = 0;
6405                     set_field_buffer(me, 1, temp);
6406                     c = '*';
6407                 } else {
6408                     c = 0;
6409                 }
6410             } else {
6411                 switch (c) {
6412                 case REQ_BEG_FIELD:
6413                 case REQ_CLR_EOF:
6414                 case REQ_CLR_EOL:
6415                 case REQ_DEL_LINE:
6416                 case REQ_DEL_WORD:
6417                 case REQ_DOWN_CHAR:
6418                 case REQ_END_FIELD:
6419                 case REQ_INS_CHAR:
6420                 case REQ_INS_LINE:
6421                 case REQ_LEFT_CHAR:
6422                 case REQ_NEW_LINE:
6423                 case REQ_NEXT_WORD:
6424                 case REQ_PREV_WORD:
6425                 case REQ_RIGHT_CHAR:
6426                 case REQ_UP_CHAR:
6427                     c = 0;      /* we don't want to do inline editing */
6428                     break;
6429                 case REQ_CLR_FIELD:
6430                     if (len) {
6431                         temp[0] = 0;
6432                         set_field_buffer(me, 1, temp);
6433                     }
6434                     break;
6435                 case REQ_DEL_CHAR:
6436                 case REQ_DEL_PREV:
6437                     if (len) {
6438                         temp[--len] = 0;
6439                         set_field_buffer(me, 1, temp);
6440                     }
6441                     break;
6442                 }
6443             }
6444             set_field_userptr(me, (void *) len);
6445             free(temp);
6446         }
6447     }
6448     return c;
6449 }
6450
6451 static int
6452 form_virtualize(FORM *f, WINDOW *w)
6453 {
6454     /* *INDENT-OFF* */
6455     static const struct {
6456         int code;
6457         int result;
6458     } lookup[] = {
6459         { CTRL('A'),    REQ_NEXT_CHOICE },
6460         { CTRL('B'),    REQ_PREV_WORD },
6461         { CTRL('C'),    REQ_CLR_EOL },
6462         { CTRL('D'),    REQ_DOWN_FIELD },
6463         { CTRL('E'),    REQ_END_FIELD },
6464         { CTRL('F'),    REQ_NEXT_PAGE },
6465         { CTRL('G'),    REQ_DEL_WORD },
6466         { CTRL('H'),    REQ_DEL_PREV },
6467         { CTRL('I'),    REQ_INS_CHAR },
6468         { CTRL('K'),    REQ_CLR_EOF },
6469         { CTRL('L'),    REQ_LEFT_FIELD },
6470         { CTRL('M'),    REQ_NEW_LINE },
6471         { CTRL('N'),    REQ_NEXT_FIELD },
6472         { CTRL('O'),    REQ_INS_LINE },
6473         { CTRL('P'),    REQ_PREV_FIELD },
6474         { CTRL('R'),    REQ_RIGHT_FIELD },
6475         { CTRL('S'),    REQ_BEG_FIELD },
6476         { CTRL('U'),    REQ_UP_FIELD },
6477         { CTRL('V'),    REQ_DEL_CHAR },
6478         { CTRL('W'),    REQ_NEXT_WORD },
6479         { CTRL('X'),    REQ_CLR_FIELD },
6480         { CTRL('Y'),    REQ_DEL_LINE },
6481         { CTRL('Z'),    REQ_PREV_CHOICE },
6482         { ESCAPE,       MAX_FORM_COMMAND + 1 },
6483         { KEY_BACKSPACE, REQ_DEL_PREV },
6484         { KEY_DOWN,     REQ_DOWN_CHAR },
6485         { KEY_END,      REQ_LAST_FIELD },
6486         { KEY_HOME,     REQ_FIRST_FIELD },
6487         { KEY_LEFT,     REQ_LEFT_CHAR },
6488         { KEY_LL,       REQ_LAST_FIELD },
6489         { KEY_NEXT,     REQ_NEXT_FIELD },
6490         { KEY_NPAGE,    REQ_NEXT_PAGE },
6491         { KEY_PPAGE,    REQ_PREV_PAGE },
6492         { KEY_PREVIOUS, REQ_PREV_FIELD },
6493         { KEY_RIGHT,    REQ_RIGHT_CHAR },
6494         { KEY_UP,       REQ_UP_CHAR },
6495         { QUIT,         MAX_FORM_COMMAND + 1 }
6496     };
6497     /* *INDENT-ON* */
6498
6499     static int mode = REQ_INS_MODE;
6500     int c = wGetchar(w);
6501     unsigned n;
6502     FIELD *me = current_field(f);
6503     bool current = TRUE;
6504
6505     if (c == CTRL(']')) {
6506         if (mode == REQ_INS_MODE) {
6507             mode = REQ_OVL_MODE;
6508         } else {
6509             mode = REQ_INS_MODE;
6510         }
6511         c = mode;
6512     } else {
6513         for (n = 0; n < SIZEOF(lookup); n++) {
6514             if (lookup[n].code == c) {
6515                 c = lookup[n].result;
6516                 break;
6517             }
6518         }
6519     }
6520     MvPrintw(0, COLS - 6, "(%s)", mode == REQ_INS_MODE ? "INS" : "OVL");
6521
6522     /*
6523      * Force the field that the user is typing into to be in reverse video,
6524      * while the other fields are shown underlined.
6525      */
6526     switch (c) {
6527     case REQ_BEG_FIELD:
6528     case REQ_CLR_EOF:
6529     case REQ_CLR_EOL:
6530     case REQ_CLR_FIELD:
6531     case REQ_DEL_CHAR:
6532     case REQ_DEL_LINE:
6533     case REQ_DEL_PREV:
6534     case REQ_DEL_WORD:
6535     case REQ_END_FIELD:
6536     case REQ_INS_CHAR:
6537     case REQ_INS_LINE:
6538     case REQ_LEFT_CHAR:
6539     case REQ_LEFT_FIELD:
6540     case REQ_NEXT_WORD:
6541     case REQ_RIGHT_CHAR:
6542         current = TRUE;
6543         break;
6544     default:
6545         current = (c < KEY_MAX);
6546         break;
6547     }
6548     if (current) {
6549         c = edit_secure(me, c);
6550         set_field_back(me, A_REVERSE);
6551     } else {
6552         c = edit_secure(me, c);
6553         set_field_back(me, A_UNDERLINE);
6554     }
6555     return c;
6556 }
6557
6558 static int
6559 my_form_driver(FORM *form, int c)
6560 {
6561     if (c == (MAX_FORM_COMMAND + 1)
6562         && form_driver(form, REQ_VALIDATION) == E_OK)
6563         return (TRUE);
6564     else {
6565         beep();
6566         return (FALSE);
6567     }
6568 }
6569
6570 #ifdef NCURSES_VERSION
6571 #define FIELDCHECK_CB(func) bool func(FIELD * fld, const void * data GCC_UNUSED)
6572 #define CHAR_CHECK_CB(func) bool func(int ch, const void *data GCC_UNUSED)
6573 #else
6574 #define FIELDCHECK_CB(func) int func(FIELD * fld, char * data GCC_UNUSED)
6575 #define CHAR_CHECK_CB(func) int func(int ch, char *data GCC_UNUSED)
6576 #endif
6577
6578 /*
6579  * Allow a middle initial, optionally with a '.' to end it.
6580  */
6581 static
6582 FIELDCHECK_CB(mi_field_check)
6583 {
6584     char *s = field_buffer(fld, 0);
6585     int state = 0;
6586     int n;
6587
6588     for (n = 0; s[n] != '\0'; ++n) {
6589         switch (state) {
6590         case 0:
6591             if (s[n] == '.') {
6592                 if (n != 1)
6593                     return FALSE;
6594                 state = 2;
6595             } else if (isspace(UChar(s[n]))) {
6596                 state = 2;
6597             }
6598             break;
6599         case 2:
6600             if (!isspace(UChar(s[n])))
6601                 return FALSE;
6602             break;
6603         }
6604     }
6605
6606     /* force the form to display a leading capital */
6607     if (islower(UChar(s[0]))) {
6608         s[0] = (char) toupper(UChar(s[0]));
6609         set_field_buffer(fld, 0, s);
6610     }
6611     return TRUE;
6612 }
6613
6614 static
6615 CHAR_CHECK_CB(mi_char_check)
6616 {
6617     return ((isalpha(ch) || ch == '.') ? TRUE : FALSE);
6618 }
6619
6620 /*
6621  * Passwords should be at least 6 characters.
6622  */
6623 static
6624 FIELDCHECK_CB(pw_field_check)
6625 {
6626     char *s = field_buffer(fld, 0);
6627     int n;
6628
6629     for (n = 0; s[n] != '\0'; ++n) {
6630         if (isspace(UChar(s[n]))) {
6631             if (n < 6)
6632                 return FALSE;
6633         }
6634     }
6635     return TRUE;
6636 }
6637
6638 static
6639 CHAR_CHECK_CB(pw_char_check)
6640 {
6641     return (isgraph(ch) ? TRUE : FALSE);
6642 }
6643
6644 static int
6645 form_test(bool recur GCC_UNUSED)
6646 {
6647     WINDOW *w;
6648     FORM *form;
6649     FIELD *f[12], *secure;
6650     FIELDTYPE *fty_middle = new_fieldtype(mi_field_check, mi_char_check);
6651     FIELDTYPE *fty_passwd = new_fieldtype(pw_field_check, pw_char_check);
6652     int finished = 0, c;
6653     unsigned n = 0;
6654
6655 #ifdef NCURSES_MOUSE_VERSION
6656     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
6657 #endif
6658
6659     move(18, 0);
6660     addstr("Defined edit/traversal keys:   ^Q/ESC- exit form\n");
6661     addstr("^N   -- go to next field       ^P  -- go to previous field\n");
6662     addstr("Home -- go to first field      End -- go to last field\n");
6663     addstr("^L   -- go to field to left    ^R  -- go to field to right\n");
6664     addstr("^U   -- move upward to field   ^D  -- move downward to field\n");
6665     addstr("^W   -- go to next word        ^B  -- go to previous word\n");
6666     addstr("^S   -- go to start of field   ^E  -- go to end of field\n");
6667     addstr("^H   -- delete previous char   ^Y  -- delete line\n");
6668     addstr("^G   -- delete current word    ^C  -- clear to end of line\n");
6669     addstr("^K   -- clear to end of field  ^X  -- clear field\n");
6670     addstr("Arrow keys move within a field as you would expect. ^] toggles overlay mode.");
6671
6672     MvAddStr(4, 57, "Forms Entry Test");
6673
6674     refresh();
6675
6676     /* describe the form */
6677     memset(f, 0, sizeof(f));
6678     f[n++] = make_label(0, 15, "Sample Form");
6679
6680     f[n++] = make_label(2, 0, "Last Name");
6681     f[n++] = make_field(3, 0, 1, 18, FALSE);
6682     set_field_type(f[n - 1], TYPE_ALPHA, 1);
6683
6684     f[n++] = make_label(2, 20, "First Name");
6685     f[n++] = make_field(3, 20, 1, 12, FALSE);
6686     set_field_type(f[n - 1], TYPE_ALPHA, 1);
6687
6688     f[n++] = make_label(2, 34, "Middle Name");
6689     f[n++] = make_field(3, 34, 1, 12, FALSE);
6690     set_field_type(f[n - 1], fty_middle);
6691
6692     f[n++] = make_label(5, 0, "Comments");
6693     f[n++] = make_field(6, 0, 4, 46, FALSE);
6694
6695     f[n++] = make_label(5, 20, "Password:");
6696     secure =
6697         f[n++] = make_field(5, 30, 1, 9, TRUE);
6698     set_field_type(f[n - 1], fty_passwd);
6699     f[n] = (FIELD *) 0;
6700
6701     if ((form = new_form(f)) != 0) {
6702
6703         display_form(form);
6704
6705         w = form_win(form);
6706         raw();
6707         nonl();                 /* lets us read ^M's */
6708         while (!finished) {
6709             switch (form_driver(form, c = form_virtualize(form, w))) {
6710             case E_OK:
6711                 MvAddStr(5, 57, field_buffer(secure, 1));
6712                 clrtoeol();
6713                 refresh();
6714                 break;
6715             case E_UNKNOWN_COMMAND:
6716                 finished = my_form_driver(form, c);
6717                 break;
6718             default:
6719                 beep();
6720                 break;
6721             }
6722         }
6723
6724         erase_form(form);
6725
6726         free_form(form);
6727     }
6728     for (c = 0; f[c] != 0; c++)
6729         free_field(f[c]);
6730     free_fieldtype(fty_middle);
6731     free_fieldtype(fty_passwd);
6732     noraw();
6733     nl();
6734
6735 #ifdef NCURSES_MOUSE_VERSION
6736     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
6737 #endif
6738     return OK;
6739 }
6740 #endif /* USE_LIBFORM */
6741
6742 /****************************************************************************
6743  *
6744  * Overlap test
6745  *
6746  ****************************************************************************/
6747
6748 #if HAVE_COPYWIN                /* ...and overlay, overwrite */
6749
6750 static const int overlap_HEAD = 1;
6751 static const int overlap_FOOT = 6;
6752
6753 static WINDOW *
6754 make_overlap(int n)
6755 {
6756     WINDOW *result;
6757     int y, x;
6758
6759     getmaxyx(stdscr, y, x);
6760     if (y < 23 || x < 80) {
6761         Cannot("The screen is too small for this test");
6762         result = 0;
6763     } else {
6764         int ymax = y - (overlap_HEAD + overlap_FOOT);
6765         int high = ymax / 5;    /* equal-sized parts for cross */
6766         int xmax = x - 2;       /* margin */
6767         int wide = (xmax / 5) & ~1;
6768         int lmar, tmar;
6769
6770         if (high > 8)
6771             high = 8;
6772
6773         if (wide > 8)
6774             wide = 8;
6775
6776         tmar = (ymax - (5 * high)) / 2 + overlap_HEAD;
6777         lmar = (xmax - (5 * wide)) / 2;
6778
6779         if (n == 0) {
6780             result = newwin(3 * high, 3 * wide, tmar, lmar);
6781         } else {
6782             result = newwin(3 * high, 3 * wide, tmar + 2 * high, lmar + 2 * wide);
6783         }
6784     }
6785     return result;
6786 }
6787
6788 static void
6789 clear_overlap(void)
6790 {
6791     int row;
6792
6793     for (row = overlap_HEAD; row < LINES - overlap_FOOT; ++row) {
6794         move(row, 0);
6795         clrtoeol();
6796     }
6797 }
6798
6799 static int
6800 move_overlap(int shift, WINDOW *win1)
6801 {
6802     int ymax = getmaxy(stdscr) - (overlap_HEAD + overlap_FOOT);
6803     int high = ymax / 5;        /* equal-sized parts for cross */
6804     int tmar;
6805     int xmax1 = getmaxx(win1) + 1;
6806     int lmar1 = (COLS - (5 * (xmax1) / 3)) / 2;
6807     int rc = ERR;
6808
6809     if (high > 8)
6810         high = 8;
6811     tmar = (ymax - (5 * high)) / 2 + overlap_HEAD;
6812
6813     rc = mvwin(win1, tmar, lmar1 + shift);
6814     return rc;
6815 }
6816
6817 static void
6818 fillwin(WINDOW *win, char ch)
6819 {
6820     int y, x;
6821     int y1, x1;
6822
6823     getmaxyx(win, y1, x1);
6824     for (y = 0; y < y1; y++) {
6825         wmove(win, y, 0);
6826         for (x = 0; x < x1; x++)
6827             waddch(win, UChar(ch));
6828     }
6829 }
6830
6831 #define InCross(x,y, x1,y1) \
6832             (((x > (x1 - 1) / 3) && (x <= (2 * (x1 - 1)) / 3)) \
6833                 || (((y > (y1 - 1) / 3) && (y <= (2 * (y1 - 1)) / 3))))
6834
6835 static void
6836 crosswin(WINDOW *win, char ch)
6837 {
6838     int y, x;
6839     int y1, x1;
6840     int xw = 1;
6841
6842     getmaxyx(win, y1, x1);
6843     for (y = 0; y < y1; y++) {
6844         for (x = 0; x < x1; x += xw) {
6845             if (InCross(x, y, x1, y1)) {
6846                 wmove(win, y, x);
6847                 waddch(win, UChar(ch));
6848             }
6849         }
6850     }
6851 }
6852
6853 /*
6854  * Match "crosswin()", but using line-drawing characters.  This could be done
6855  * a little simpler using box(), but the reason for this example is to test
6856  * hline/vline and addch with line-drawing vs the copy/overlay functions.
6857  */
6858 static void
6859 crossbox(WINDOW *win)
6860 {
6861     int y1, x1;
6862     int ymax, xmax;
6863
6864     getmaxyx(win, y1, x1);
6865
6866     ymax = (y1 + 1);
6867     xmax = (x1 + 1);
6868
6869     mvwhline(win, 0, (xmax / 3), ACS_HLINE, (xmax / 3));
6870     mvwhline(win, ymax / 3, 0, ACS_HLINE, xmax);
6871     mvwhline(win, ((2 * ymax) / 3) - 1, 0, ACS_HLINE, xmax);
6872     mvwhline(win, y1 - 1, (xmax / 3), ACS_HLINE, (xmax / 3));
6873
6874     mvwvline(win, (ymax / 3), 0, ACS_VLINE, (ymax / 3));
6875     mvwvline(win, 0, xmax / 3, ACS_VLINE, ymax);
6876     mvwvline(win, 0, ((2 * xmax) / 3) - 1, ACS_VLINE, ymax);
6877     mvwvline(win, (ymax / 3), x1 - 1, ACS_VLINE, (ymax / 3));
6878
6879     mvwaddch(win, 0, (xmax / 3), ACS_ULCORNER);
6880     mvwaddch(win, 0, ((2 * xmax) / 3) - 1, ACS_URCORNER);
6881     mvwaddch(win, y1 - 1, (xmax / 3), ACS_LLCORNER);
6882     mvwaddch(win, y1 - 1, ((2 * xmax) / 3) - 1, ACS_LRCORNER);
6883
6884     mvwaddch(win, (ymax / 3), 0, ACS_ULCORNER);
6885     mvwaddch(win, ((2 * ymax) / 3) - 1, 0, ACS_LLCORNER);
6886     mvwaddch(win, (ymax / 3), x1 - 1, ACS_URCORNER);
6887     mvwaddch(win, ((2 * ymax) / 3) - 1, x1 - 1, ACS_LRCORNER);
6888
6889     mvwaddch(win, (ymax / 3), (xmax / 3), ACS_PLUS);
6890     mvwaddch(win, (ymax / 3), ((2 * xmax) / 3) - 1, ACS_PLUS);
6891     mvwaddch(win, ((2 * ymax) / 3) - 1, ((2 * xmax) / 3) - 1, ACS_PLUS);
6892     mvwaddch(win, ((2 * ymax) / 3) - 1, (xmax / 3), ACS_PLUS);
6893 }
6894
6895 typedef enum {
6896     otBASE_refresh = 0
6897     ,otBASE_fill
6898     ,otBASE_draw
6899     ,otBASE_clear
6900     ,otBASE_copy
6901 } otBASE;
6902
6903 #define OVERLAP_FLAVORS 6
6904
6905 typedef enum {
6906     otFILL_normal = 0
6907     ,otFILL_bold
6908     ,otFILL_color
6909     ,otFILL_bright
6910 } otFILL;
6911
6912 #define LimitFILL() UseColors ? 4 : 2
6913
6914 typedef enum {
6915     otDRAW_text_cross = 0
6916     ,otDRAW_line_box
6917     ,otDRAW_line_cross
6918     ,otDRAW_set_bg
6919     ,otDRAW_reset_bg
6920 } otDRAW;
6921
6922 #define LimitDRAW() UseColors ? 5 : 3
6923
6924 typedef enum {
6925     otCOPY_overwrite = 0
6926     ,otCOPY_merge
6927     ,otCOPY_force
6928     ,otCOPY_overlay
6929 } otCOPY;
6930
6931 #define LimitCOPY() 4
6932
6933 static void
6934 overlap_helpitem(int state, int item, char *message)
6935 {
6936     int row = (item / 2);
6937     int col = ((item % 2) ? COLS / 2 : 0);
6938
6939     move(LINES - 6 + row, col);
6940     printw("%c%c = %s", state == row ? '>' : ' ', 'a' + item, message);
6941     clrtoeol();
6942 }
6943
6944 static void
6945 overlap_test_1_attr(WINDOW *win, int flavor, int col)
6946 {
6947     NCURSES_PAIRS_T cpair = (NCURSES_PAIRS_T) (1 + (flavor * 2) + col);
6948
6949     switch ((otFILL) flavor) {
6950     case otFILL_normal:
6951         (void) wattrset(win, A_NORMAL);
6952         break;
6953     case otFILL_bold:
6954         (void) wattrset(win, A_BOLD);
6955         break;
6956     case otFILL_color:
6957         init_pair(cpair, COLOR_BLUE, COLOR_WHITE);
6958         (void) wattrset(win, AttrArg(COLOR_PAIR(cpair), A_NORMAL));
6959         break;
6960     case otFILL_bright:
6961         init_pair(cpair, COLOR_WHITE, COLOR_BLUE);
6962         (void) wattrset(win, AttrArg(COLOR_PAIR(cpair), A_BOLD));
6963         break;
6964     }
6965 }
6966
6967 static void
6968 overlap_test_2_attr(WINDOW *win, int flavor, int col)
6969 {
6970     NCURSES_PAIRS_T cpair = (NCURSES_PAIRS_T) (9 + (flavor * 2) + col);
6971
6972     switch ((otDRAW) flavor) {
6973     case otDRAW_text_cross:
6974         /* no effect */
6975         break;
6976     case otDRAW_line_box:
6977         /* no effect */
6978         break;
6979     case otDRAW_line_cross:
6980         /* no effect */
6981         break;
6982     case otDRAW_set_bg:
6983         init_pair(cpair, COLOR_RED, COLOR_GREEN);
6984         wbkgdset(win, colored_chtype(' ', A_BLINK, cpair));
6985         break;
6986     case otDRAW_reset_bg:
6987         wbkgdset(win, ' ' | A_NORMAL);
6988         break;
6989     }
6990 }
6991
6992 static int
6993 overlap_help(int state, int flavors[OVERLAP_FLAVORS])
6994 {
6995     int row;
6996     int col;
6997     int item;
6998     int limit[OVERLAP_FLAVORS];
6999     const char *ths, *tht;
7000     char msg[80];
7001
7002     if (state < 0)
7003         state += OVERLAP_FLAVORS;
7004     state = state % OVERLAP_FLAVORS;
7005     assert(state >= 0 && state < OVERLAP_FLAVORS);
7006
7007     for (item = 0; item < (2 * OVERLAP_FLAVORS); ++item) {
7008         row = item / 2;
7009         col = item % 2;
7010         ths = col ? "B" : "A";
7011         tht = col ? "A" : "B";
7012
7013         switch ((otBASE) row) {
7014         case otBASE_refresh:
7015             limit[row] = 1;
7016             flavors[row] = 0;
7017             _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7018                         "refresh %s, then %s, then doupdate.", ths, tht);
7019             break;
7020         case otBASE_fill:
7021             limit[row] = LimitFILL();
7022             flavors[row] %= limit[row];
7023             overlap_test_1_attr(stdscr, flavors[row], col);
7024             _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7025                         "fill window %s with letter %s.", ths, ths);
7026             break;
7027         case otBASE_draw:
7028             limit[row] = LimitDRAW();
7029             flavors[row] %= limit[row];
7030             switch ((otDRAW) flavors[row]) {
7031             case otDRAW_text_cross:
7032                 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7033                             "cross text-pattern in window %s.", ths);
7034                 break;
7035             case otDRAW_line_box:
7036                 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7037                             "draw line-box in window %s.", ths);
7038                 break;
7039             case otDRAW_line_cross:
7040                 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7041                             "draw line-cross in window %s.", ths);
7042                 break;
7043             case otDRAW_set_bg:
7044                 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7045                             "set background of window %s.", ths);
7046                 break;
7047             case otDRAW_reset_bg:
7048                 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7049                             "reset background of window %s.", ths);
7050                 break;
7051             }
7052             break;
7053         case otBASE_clear:
7054             limit[row] = 1;
7055             flavors[row] = 0;
7056             _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7057                         "clear window %s.", ths);
7058             break;
7059         case otBASE_copy:
7060             limit[row] = LimitCOPY();
7061             flavors[row] %= limit[row];
7062             switch ((otCOPY) flavors[row]) {
7063             case otCOPY_overwrite:
7064                 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7065                             "overwrite %s onto %s.", ths, tht);
7066                 break;
7067             case otCOPY_merge:
7068                 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7069                             "copywin(FALSE) %s onto %s.", ths, tht);
7070                 break;
7071             case otCOPY_force:
7072                 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7073                             "copywin(TRUE) %s onto %s.", ths, tht);
7074                 break;
7075             case otCOPY_overlay:
7076                 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7077                             "overlay %s onto %s.", ths, tht);
7078                 break;
7079             }
7080             break;
7081         }
7082         overlap_helpitem(state, item, msg);
7083         (void) wattrset(stdscr, A_NORMAL);
7084         wbkgdset(stdscr, ' ' | A_NORMAL);
7085     }
7086     move(LINES - 1, 0);
7087     printw("^Q/ESC = terminate test. </> shift. Up/down/space select (row %d",
7088            state + 1);
7089     if (limit[state] > 1)
7090         printw(" test %d:%d", 1 + flavors[state], limit[state]);
7091     printw(").");
7092     clrtoeol();
7093
7094     return state;
7095 }
7096
7097 static void
7098 overlap_test_0(WINDOW *a, WINDOW *b)
7099 {
7100     touchwin(a);
7101     touchwin(b);
7102     wnoutrefresh(a);
7103     wnoutrefresh(b);
7104     doupdate();
7105 }
7106
7107 static void
7108 overlap_test_1(int flavor, int col, WINDOW *a, char fill)
7109 {
7110     overlap_test_1_attr(a, flavor, col);
7111     fillwin(a, fill);
7112     (void) wattrset(a, A_NORMAL);
7113 }
7114
7115 static void
7116 overlap_test_2(int flavor, int col, WINDOW *a, char fill)
7117 {
7118     overlap_test_2_attr(a, flavor, col);
7119     switch ((otDRAW) flavor) {
7120     case otDRAW_text_cross:
7121         crosswin(a, fill);
7122         break;
7123     case otDRAW_line_box:
7124         box(a, 0, 0);
7125         break;
7126     case otDRAW_line_cross:
7127         crossbox(a);
7128         break;
7129     case otDRAW_set_bg:
7130         /* done in overlap_test_2_attr */
7131         break;
7132     case otDRAW_reset_bg:
7133         /* done in overlap_test_2_attr */
7134         break;
7135     }
7136 }
7137
7138 static void
7139 overlap_test_3(WINDOW *a)
7140 {
7141     wclear(a);
7142     wmove(a, 0, 0);
7143 }
7144
7145 static void
7146 overlap_test_4(int flavor, WINDOW *a, WINDOW *b)
7147 {
7148     switch ((otCOPY) flavor) {
7149     case otCOPY_overwrite:
7150         overwrite(a, b);
7151         break;
7152     case otCOPY_merge:
7153         copywin(a, b, 0, 0, 0, 0, getmaxy(b), getmaxx(b), FALSE);
7154         break;
7155     case otCOPY_force:
7156         copywin(a, b, 0, 0, 0, 0, getmaxy(b), getmaxx(b), TRUE);
7157         break;
7158     case otCOPY_overlay:
7159         overlay(a, b);
7160         break;
7161     }
7162 }
7163
7164 /* test effects of overlapping windows */
7165 static int
7166 overlap_test(bool recur GCC_UNUSED)
7167 {
7168     WINDOW *win1, *win2;
7169     int ch;
7170     int shift = 0, last_refresh = -1;
7171     int state, flavor[OVERLAP_FLAVORS];
7172
7173     if ((win1 = make_overlap(0)) == 0) {
7174         return ERR;
7175     } else if ((win2 = make_overlap(1)) == 0) {
7176         delwin(win1);
7177         return ERR;
7178     }
7179
7180     curs_set(0);
7181     raw();
7182     refresh();
7183     move(0, 0);
7184     printw("Test wnoutrefresh() for two overlapping windows:");
7185
7186     memset(flavor, 0, sizeof(flavor));
7187     state = overlap_help(0, flavor);
7188
7189     while (!isQuit(ch = Getchar(), TRUE)) {
7190         switch (ch) {
7191         case 'a':               /* refresh window A first, then B */
7192             overlap_test_0(win1, win2);
7193             break;
7194
7195         case 'b':               /* refresh window B first, then A */
7196             overlap_test_0(win2, win1);
7197             break;
7198
7199         case 'c':               /* fill window A so it's visible */
7200             overlap_test_1(flavor[otBASE_fill], 0, win1, 'A');
7201             break;
7202
7203         case 'd':               /* fill window B so it's visible */
7204             overlap_test_1(flavor[otBASE_fill], 1, win2, 'B');
7205             break;
7206
7207         case 'e':               /* cross test pattern in window A */
7208             overlap_test_2(flavor[otBASE_draw], 0, win1, 'A');
7209             break;
7210
7211         case 'f':               /* cross test pattern in window A */
7212             overlap_test_2(flavor[otBASE_draw], 1, win2, 'B');
7213             break;
7214
7215         case 'g':               /* clear window A */
7216             overlap_test_3(win1);
7217             break;
7218
7219         case 'h':               /* clear window B */
7220             overlap_test_3(win2);
7221             break;
7222
7223         case 'i':               /* overwrite A onto B */
7224             overlap_test_4(flavor[otBASE_copy], win1, win2);
7225             break;
7226
7227         case 'j':               /* overwrite B onto A */
7228             overlap_test_4(flavor[otBASE_copy], win2, win1);
7229             break;
7230
7231         case CTRL('n'):
7232         case KEY_DOWN:
7233             state = overlap_help(state + 1, flavor);
7234             break;
7235
7236         case CTRL('p'):
7237         case KEY_UP:
7238             state = overlap_help(state - 1, flavor);
7239             break;
7240
7241         case ' ':
7242             flavor[state] += 1;
7243             state = overlap_help(state, flavor);
7244             break;
7245
7246         case HELP_KEY_1:
7247             state = overlap_help(state, flavor);
7248             break;
7249
7250         case '<':
7251             /* FALLTHRU */
7252         case '>':
7253             /* see below */
7254             break;
7255
7256         default:
7257             beep();
7258             break;
7259         }
7260
7261         switch (ch) {
7262         case 'a':
7263             /* FALLTHRU */
7264         case 'b':
7265             last_refresh = ch;
7266             break;
7267         case '<':
7268             shift -= 2;
7269             /* FALLTHRU */
7270         case '>':
7271             shift += 1;
7272             if (move_overlap(shift, win1) != OK) {
7273                 flash();
7274                 shift += (ch == '>') ? -1 : 1;
7275             } else if (last_refresh > 0) {
7276                 clear_overlap();
7277                 wnoutrefresh(stdscr);
7278                 if (last_refresh == 'a')
7279                     overlap_test_0(win1, win2);
7280                 else
7281                     overlap_test_0(win2, win1);
7282             }
7283             break;
7284         default:
7285             last_refresh = -1;
7286             break;
7287         }
7288     }
7289
7290     delwin(win2);
7291     delwin(win1);
7292     erase();
7293     exit_curses();
7294     return OK;
7295 }
7296
7297 #if USE_WIDEC_SUPPORT
7298 static void
7299 x_fillwin(WINDOW *win, wchar_t ch)
7300 {
7301     int y, x;
7302     int y1, x1;
7303
7304     getmaxyx(win, y1, x1);
7305     x1 /= 2;
7306     for (y = 0; y < y1; y++) {
7307         wmove(win, y, 0);
7308         for (x = 0; x < x1; x++)
7309             waddnwstr(win, &ch, 1);
7310     }
7311 }
7312
7313 static void
7314 x_crosswin(WINDOW *win, wchar_t ch)
7315 {
7316     int y, x;
7317     int y1, x1;
7318     int xw = 2;
7319
7320     getmaxyx(win, y1, x1);
7321     for (y = 0; y < y1; y++) {
7322         for (x = 0; x < x1; x += xw) {
7323             if (InCross(x, y, x1, y1)) {
7324                 wmove(win, y, x);
7325                 waddnwstr(win, &ch, 1);
7326             }
7327         }
7328     }
7329 }
7330
7331 static void
7332 x_overlap_test_1(int flavor, int col, WINDOW *a, wchar_t fill)
7333 {
7334     overlap_test_1_attr(a, flavor, col);
7335     x_fillwin(a, fill);
7336     (void) wattrset(a, A_NORMAL);
7337 }
7338
7339 static void
7340 x_overlap_test_2(int flavor, int col, WINDOW *a, wchar_t fill)
7341 {
7342     overlap_test_2_attr(a, flavor, col);
7343     switch ((otDRAW) flavor) {
7344     case otDRAW_text_cross:
7345         x_crosswin(a, fill);
7346         break;
7347     case otDRAW_line_box:
7348         box(a, 0, 0);
7349         break;
7350     case otDRAW_line_cross:
7351         crossbox(a);
7352         break;
7353     case otDRAW_set_bg:
7354         /* done in overlap_test_2_attr */
7355         break;
7356     case otDRAW_reset_bg:
7357         /* done in overlap_test_2_attr */
7358         break;
7359     }
7360 }
7361
7362 /* test effects of overlapping windows */
7363 static int
7364 x_overlap_test(bool recur GCC_UNUSED)
7365 {
7366     const wchar_t WIDE_A = 0xff21;
7367     const wchar_t WIDE_B = 0xff22;
7368     WINDOW *win1, *win2;
7369     int ch;
7370     int shift = 0, last_refresh = -1;
7371     int state, flavor[OVERLAP_FLAVORS];
7372
7373     if ((win1 = make_overlap(0)) == 0) {
7374         return ERR;
7375     } else if ((win2 = make_overlap(1)) == 0) {
7376         delwin(win1);
7377         return ERR;
7378     }
7379
7380     curs_set(0);
7381     raw();
7382     refresh();
7383     move(0, 0);
7384     printw("Test wnoutrefresh() for overlapping windows with double-cell characters:");
7385
7386     memset(flavor, 0, sizeof(flavor));
7387     state = overlap_help(0, flavor);
7388
7389     while (!isQuit(ch = Getchar(), TRUE)) {
7390         switch (ch) {
7391         case 'a':               /* refresh window A first, then B */
7392             overlap_test_0(win1, win2);
7393             break;
7394
7395         case 'b':               /* refresh window B first, then A */
7396             overlap_test_0(win2, win1);
7397             break;
7398
7399         case 'c':               /* fill window A so it's visible */
7400             x_overlap_test_1(flavor[otBASE_fill], 0, win1, WIDE_A);
7401             break;
7402
7403         case 'd':               /* fill window B so it's visible */
7404             x_overlap_test_1(flavor[otBASE_fill], 1, win2, WIDE_B);
7405             break;
7406
7407         case 'e':               /* cross test pattern in window A */
7408             x_overlap_test_2(flavor[otBASE_draw], 0, win1, WIDE_A);
7409             break;
7410
7411         case 'f':               /* cross test pattern in window A */
7412             x_overlap_test_2(flavor[otBASE_draw], 1, win2, WIDE_B);
7413             break;
7414
7415         case 'g':               /* clear window A */
7416             overlap_test_3(win1);
7417             break;
7418
7419         case 'h':               /* clear window B */
7420             overlap_test_3(win2);
7421             break;
7422
7423         case 'i':               /* overwrite A onto B */
7424             overlap_test_4(flavor[otBASE_copy], win1, win2);
7425             break;
7426
7427         case 'j':               /* overwrite B onto A */
7428             overlap_test_4(flavor[otBASE_copy], win2, win1);
7429             break;
7430
7431         case CTRL('n'):
7432         case KEY_DOWN:
7433             state = overlap_help(state + 1, flavor);
7434             break;
7435
7436         case CTRL('p'):
7437         case KEY_UP:
7438             state = overlap_help(state - 1, flavor);
7439             break;
7440
7441         case ' ':
7442             flavor[state] += 1;
7443             state = overlap_help(state, flavor);
7444             break;
7445
7446         case HELP_KEY_1:
7447             state = overlap_help(state, flavor);
7448             break;
7449
7450         case '<':
7451             /* FALLTHRU */
7452         case '>':
7453             /* see below */
7454             break;
7455
7456         default:
7457             beep();
7458             break;
7459         }
7460
7461         switch (ch) {
7462         case 'a':
7463             /* FALLTHRU */
7464         case 'b':
7465             last_refresh = ch;
7466             break;
7467         case '<':
7468             shift -= 2;
7469             /* FALLTHRU */
7470         case '>':
7471             shift += 1;
7472             if (move_overlap(shift, win1) != OK) {
7473                 flash();
7474                 shift += (ch == '>') ? -1 : 1;
7475             } else if (last_refresh > 0) {
7476                 clear_overlap();
7477                 wnoutrefresh(stdscr);
7478                 if (last_refresh == 'a')
7479                     overlap_test_0(win1, win2);
7480                 else
7481                     overlap_test_0(win2, win1);
7482             }
7483             break;
7484         default:
7485             last_refresh = -1;
7486             break;
7487         }
7488     }
7489
7490     delwin(win2);
7491     delwin(win1);
7492     erase();
7493     exit_curses();
7494     return OK;
7495 }
7496 #endif /* USE_WIDEC_SUPPORT */
7497
7498 #endif /* HAVE_COPYWIN */
7499
7500 static void
7501 show_setting_name(const char *name)
7502 {
7503     printw("%-25s ", name);
7504 }
7505
7506 static void
7507 show_string_setting(const char *name, const char *value)
7508 {
7509     show_setting_name(name);
7510     if (value) {
7511         printw("\"%s\"", value);
7512     } else {
7513         attron(A_REVERSE);
7514         addstr("<NULL>");
7515         attroff(A_REVERSE);
7516     }
7517     AddCh('\n');
7518 }
7519
7520 static void
7521 show_number_setting(const char *name, int value)
7522 {
7523     show_setting_name(name);
7524     if (value >= 0) {
7525         printw("%d", value);
7526     } else {
7527         attron(A_REVERSE);
7528         printw("%d", value);
7529         attroff(A_REVERSE);
7530     }
7531     AddCh('\n');
7532 }
7533
7534 static void
7535 show_boolean_setting(const char *name, int value)
7536 {
7537     show_setting_name(name);
7538     if (value >= 0) {
7539         printw("%s", value ? "TRUE" : "FALSE");
7540     } else {
7541         attron(A_REVERSE);
7542         printw("%d", value);
7543         attroff(A_REVERSE);
7544     }
7545     AddCh('\n');
7546 }
7547
7548 static int
7549 settings_test(bool recur GCC_UNUSED)
7550 {
7551 #if USE_WIDEC_SUPPORT
7552     wchar_t ch;
7553 #endif
7554
7555     move(0, 0);
7556     show_string_setting("termname", termname());
7557     show_string_setting("longname", longname());
7558     show_number_setting("baudrate", baudrate());
7559     if (erasechar() > 0) {
7560         show_string_setting("unctrl(erasechar)", unctrl((chtype) erasechar()));
7561         show_string_setting("keyname(erasechar)", keyname(erasechar()));
7562     }
7563     if (killchar() > 0) {
7564         show_string_setting("unctrl(killchar)", unctrl((chtype) killchar()));
7565         show_string_setting("keyname(killchar)", keyname(killchar()));
7566     }
7567 #if USE_WIDEC_SUPPORT
7568     if (erasewchar(&ch) == OK) {
7569         show_string_setting("key_name(erasewchar)", key_name(ch));
7570     }
7571     if (killwchar(&ch) == OK) {
7572         show_string_setting("key_name(killwchar)", key_name(ch));
7573     }
7574 #endif
7575     show_boolean_setting("has_ic", has_ic());
7576     show_boolean_setting("has_il", has_il());
7577     show_boolean_setting("has_colors", has_colors());
7578 #if HAVE_COLOR_CONTENT
7579     show_boolean_setting("can_change_color", can_change_color());
7580 #endif
7581     Pause();
7582     erase();
7583     exit_curses();
7584     return OK;
7585 }
7586
7587 /****************************************************************************
7588  *
7589  * Main sequence
7590  *
7591  ****************************************************************************/
7592
7593 static void
7594 usage(void)
7595 {
7596     static const char *const tbl[] =
7597     {
7598         "Usage: ncurses [options]"
7599         ,""
7600         ,"Options:"
7601 #ifdef NCURSES_VERSION
7602         ,"  -a f,b   set default-colors (assumed white-on-black)"
7603         ,"  -d       use default-colors if terminal supports them"
7604 #endif
7605 #if HAVE_USE_ENV
7606         ,"  -E       call use_env(FALSE) to ignore $LINES and $COLUMNS"
7607 #endif
7608 #if USE_SOFTKEYS
7609         ,"  -e fmt   specify format for soft-keys test (e)"
7610 #endif
7611 #if HAVE_RIPOFFLINE
7612         ,"  -f       rip-off footer line (can repeat)"
7613         ,"  -h       rip-off header line (can repeat)"
7614 #endif
7615         ,"  -m       do not use colors"
7616 #if HAVE_COLOR_CONTENT
7617         ,"  -p file  rgb values to use in 'd' rather than ncurses's builtin"
7618 #endif
7619 #if USE_LIBPANEL
7620         ,"  -s msec  specify nominal time for panel-demo (default: 1, to hold)"
7621 #endif
7622 #if defined(NCURSES_VERSION_PATCH) && (NCURSES_VERSION_PATCH >= 20120714) && !defined(_WIN32)
7623         ,"  -T       call use_tioctl(TRUE) to allow SIGWINCH to override environment"
7624 #endif
7625 #ifdef TRACE
7626         ,"  -t mask  specify default trace-level (may toggle with ^T)"
7627 #endif
7628 #if HAVE_COLOR_CONTENT
7629         ,"  -x       use xterm-compatible control for reading color palette"
7630 #endif
7631     };
7632     size_t n;
7633     for (n = 0; n < SIZEOF(tbl); n++)
7634         fprintf(stderr, "%s\n", tbl[n]);
7635     ExitProgram(EXIT_FAILURE);
7636 }
7637
7638 static void
7639 set_terminal_modes(void)
7640 {
7641     noraw();
7642     cbreak();
7643     noecho();
7644     scrollok(stdscr, TRUE);
7645     idlok(stdscr, TRUE);
7646     keypad(stdscr, TRUE);
7647 }
7648
7649 #ifdef SIGUSR1
7650 static void
7651 announce_sig(int sig)
7652 {
7653     (void) fprintf(stderr, "Handled signal %d\r\n", sig);
7654 }
7655 #endif
7656
7657 #if HAVE_RIPOFFLINE
7658 static int
7659 rip_footer(WINDOW *win, int cols)
7660 {
7661     wbkgd(win, A_REVERSE);
7662     werase(win);
7663     wmove(win, 0, 0);
7664     wprintw(win, "footer: window %p, %d columns", (void *) win, cols);
7665     wnoutrefresh(win);
7666     return OK;
7667 }
7668
7669 static int
7670 rip_header(WINDOW *win, int cols)
7671 {
7672     wbkgd(win, A_REVERSE);
7673     werase(win);
7674     wmove(win, 0, 0);
7675     wprintw(win, "header: window %p, %d columns", (void *) win, cols);
7676     wnoutrefresh(win);
7677     return OK;
7678 }
7679 #endif /* HAVE_RIPOFFLINE */
7680
7681 static void
7682 main_menu(bool top)
7683 {
7684 #if USE_WIDEC_SUPPORT
7685     typedef struct {
7686         bool recur;
7687         int (*narrow_func) (bool);
7688         int (*wide_func) (bool);
7689         int code;
7690         const char *help;
7691     } MyCmds;
7692 #define BOTH(a)   a, x_ ## a
7693 #define ONLY(a)   a, NULL
7694 #define CMDS(recur, funcs,code,help) { recur, funcs, code, help }
7695 #else
7696     typedef struct {
7697         bool recur;
7698         int (*narrow_func) (bool);
7699         int code;
7700         const char *help;
7701     } MyCmds;
7702 #define BOTH(a)   a
7703 #define ONLY(a)   a
7704 #define CMDS(recur, funcs,code,help) { recur, funcs, code, help }
7705 #endif
7706     /* *INDENT-OFF* */
7707     static MyCmds cmds[] =
7708     {
7709         CMDS(TRUE, BOTH(getch_test),    'a', "keyboard and mouse input test"),
7710         CMDS(TRUE, BOTH(attr_test),     'b', "character attribute test"),
7711         CMDS(TRUE, BOTH(color_test),    'c', "color test pattern"),
7712 #if HAVE_COLOR_CONTENT
7713         CMDS(FALSE, ONLY(color_edit),   'd', "edit RGB color values"),
7714 #endif
7715 #if USE_SOFTKEYS
7716         CMDS(TRUE, BOTH(slk_test),      'e', "exercise soft keys"),
7717 #endif
7718         CMDS(TRUE, BOTH(acs_test),      'f', "display ACS characters"),
7719         CMDS(TRUE, ONLY(scroll_test),   'g', "display windows and scrolling"),
7720         CMDS(TRUE, ONLY(flushinp_test), 'i', "test flushinp()"),
7721         CMDS(TRUE, ONLY(sgr_attr_test), 'k', "display character attributes"),
7722 #if USE_LIBMENU
7723         CMDS(TRUE, ONLY(menu_test),     'm', "exercise menu library"),
7724 #endif
7725 #if USE_LIBPANEL
7726         CMDS(TRUE, BOTH(panel_test),    'o', "exercise panel library"),
7727 #endif
7728 #if HAVE_NEWPAD
7729         CMDS(TRUE, ONLY(pad_test),      'p', "exercise pad features"),
7730 #endif
7731         CMDS(TRUE, ONLY(NULL),          'q', "quit"),
7732 #if USE_LIBMENU
7733         CMDS(TRUE, ONLY(form_test),     'r', "exercise form library"),
7734 #endif
7735 #if HAVE_COPYWIN
7736         CMDS(TRUE, BOTH(overlap_test),  's', "overlapping-refresh test"),
7737 #endif
7738 #if USE_LIBMENU && defined(TRACE)
7739         CMDS(TRUE, ONLY(trace_set),     't', "set trace level"),
7740 #endif
7741         CMDS(TRUE, ONLY(settings_test), 'v', "show terminal name and settings"),
7742         CMDS(FALSE, ONLY(NULL),         '?', "repeat this command summary")
7743     };
7744     /* *INDENT-ON* */
7745
7746     int (*doit) (bool);
7747     char command;
7748     unsigned n;
7749
7750     do {
7751         printf("This is the ncurses main menu (uppercase for wide-characters)\n");
7752         for (n = 0; n < SIZEOF(cmds); ++n) {
7753             if (top || cmds[n].recur) {
7754                 putchar(' ');
7755 #if USE_WIDEC_SUPPORT
7756                 if (cmds[n].wide_func) {
7757                     printf("%c,", toupper(cmds[n].code));
7758                 }
7759 #endif
7760                 printf("%c\t= %s\n", cmds[n].code, cmds[n].help);
7761             }
7762         }
7763
7764         (void) fputs("> ", stdout);
7765         (void) fflush(stdout);  /* necessary under SVr4 curses */
7766
7767         /*
7768          * This used to be an 'fgets()' call (until 1996/10).  However with
7769          * some runtime libraries, mixing stream I/O and 'read()' causes the
7770          * input stream to be flushed when switching between the two.
7771          */
7772         command = 0;
7773         for (;;) {
7774             char ch = '\0';
7775             if (read(fileno(stdin), &ch, (size_t) 1) <= 0) {
7776                 if (command == 0)
7777                     command = 'q';
7778                 break;
7779             } else if (command == 0 && !isspace(UChar(ch))) {
7780                 command = ch;
7781             } else if (ch == '\n' || ch == '\r') {
7782                 if ((command == 'd') && !top) {
7783                     (void) fputs("Do not nest test-d\n", stdout);
7784                     command = 0;
7785                 }
7786                 if (command != 0)
7787                     break;
7788                 (void) fputs("> ", stdout);
7789                 (void) fflush(stdout);
7790             }
7791         }
7792
7793         doit = NULL;
7794         for (n = 0; n < SIZEOF(cmds); ++n) {
7795             if (cmds[n].code == command) {
7796                 doit = cmds[n].narrow_func;
7797                 break;
7798             }
7799 #if USE_WIDEC_SUPPORT
7800             if (toupper(cmds[n].code) == command) {
7801                 doit = cmds[n].wide_func;
7802                 break;
7803             }
7804 #endif
7805         }
7806
7807         if (doit != NULL && doit(FALSE) == OK) {
7808             /*
7809              * This may be overkill; it's intended to reset everything back
7810              * to the initial terminal modes so that tests don't get in
7811              * each other's way.
7812              */
7813             flushinp();
7814             set_terminal_modes();
7815             reset_prog_mode();
7816             clear();
7817             refresh();
7818             endwin();
7819             if (command == '?') {
7820                 (void) puts("This is the ncurses capability tester.");
7821                 (void)
7822                     puts("You may select a test from the main menu by typing the");
7823                 (void)
7824                     puts("key letter of the choice (the letter to left of the =)");
7825                 (void)
7826                     puts("at the > prompt.  Type `q' to exit.");
7827             }
7828             continue;
7829         }
7830     } while
7831         (command != 'q');
7832 }
7833
7834 /*+-------------------------------------------------------------------------
7835         main(argc,argv)
7836 --------------------------------------------------------------------------*/
7837
7838 int
7839 main(int argc, char *argv[])
7840 {
7841     int c;
7842     int my_e_param = 1;
7843 #ifdef NCURSES_VERSION
7844     int default_fg = COLOR_WHITE;
7845     int default_bg = COLOR_BLACK;
7846     bool assumed_colors = FALSE;
7847     bool default_colors = FALSE;
7848 #endif
7849     bool monochrome = FALSE;
7850 #if HAVE_COLOR_CONTENT
7851     bool xterm_colors = FALSE;
7852     char *palette_file = 0;
7853 #endif
7854
7855     setlocale(LC_ALL, "");
7856
7857     while ((c = getopt(argc, argv, "a:dEe:fhmp:s:Tt:x")) != -1) {
7858         switch (c) {
7859 #ifdef NCURSES_VERSION
7860         case 'a':
7861             assumed_colors = TRUE;
7862             switch (sscanf(optarg, "%d,%d", &default_fg, &default_bg)) {
7863             case 0:
7864                 default_fg = COLOR_WHITE;
7865                 /* FALLTHRU */
7866             case 1:
7867                 default_bg = COLOR_BLACK;
7868                 break;
7869             }
7870             break;
7871         case 'd':
7872             default_colors = TRUE;
7873             break;
7874 #endif
7875 #if HAVE_USE_ENV
7876         case 'E':
7877             use_env(FALSE);
7878             break;
7879 #endif
7880         case 'e':
7881             my_e_param = atoi(optarg);
7882 #ifdef NCURSES_VERSION
7883             if (my_e_param > 3) /* allow extended layouts */
7884                 usage();
7885 #else
7886             if (my_e_param > 1)
7887                 usage();
7888 #endif
7889             break;
7890 #if HAVE_RIPOFFLINE
7891         case 'f':
7892             ripoffline(-1, rip_footer);
7893             break;
7894         case 'h':
7895             ripoffline(1, rip_header);
7896             break;
7897 #endif /* HAVE_RIPOFFLINE */
7898         case 'm':
7899             monochrome = TRUE;
7900             break;
7901 #if HAVE_COLOR_CONTENT
7902         case 'p':
7903             palette_file = optarg;
7904             break;
7905 #endif
7906 #if USE_LIBPANEL
7907         case 's':
7908             nap_msec = (int) atol(optarg);
7909             break;
7910 #endif
7911 #if defined(NCURSES_VERSION_PATCH) && (NCURSES_VERSION_PATCH >= 20120714) && !defined(_WIN32)
7912         case 'T':
7913             use_tioctl(TRUE);
7914             break;
7915 #endif
7916 #ifdef TRACE
7917         case 't':
7918             save_trace = (unsigned) strtol(optarg, 0, 0);
7919             break;
7920 #endif
7921 #if HAVE_COLOR_CONTENT
7922         case 'x':
7923             xterm_colors = TRUE;
7924             break;
7925 #endif
7926         default:
7927             usage();
7928         }
7929     }
7930
7931     /*
7932      * If there's no menus (unlikely for ncurses!), then we'll have to set
7933      * tracing on initially, just in case the user wants to test something that
7934      * doesn't involve wGetchar.
7935      */
7936 #ifdef TRACE
7937     /* enable debugging */
7938 #if !USE_LIBMENU
7939     trace(save_trace);
7940 #else
7941     if (!isatty(fileno(stdin)))
7942         trace(save_trace);
7943 #endif /* USE_LIBMENU */
7944 #endif /* TRACE */
7945
7946 #if USE_SOFTKEYS
7947     /* tell it we're going to play with soft keys */
7948     slk_init(my_e_param);
7949 #endif
7950
7951 #ifdef SIGUSR1
7952     /* set up null signal catcher so we can see what interrupts to getch do */
7953     signal(SIGUSR1, announce_sig);
7954 #endif
7955
7956     /* we must initialize the curses data structure only once */
7957     initscr();
7958     bkgdset(BLANK);
7959
7960     set_terminal_modes();
7961     def_prog_mode();
7962
7963     /* tests, in general, will want these modes */
7964     UseColors = (bool) (monochrome ? FALSE : has_colors());
7965
7966     if (UseColors) {
7967         start_color();
7968 #ifdef NCURSES_VERSION_PATCH
7969         MaxColors = COLORS;     /* was > 16 ? 16 : COLORS */
7970 #if HAVE_USE_DEFAULT_COLORS
7971         if (default_colors) {
7972             use_default_colors();
7973             MinColors = -1;
7974         }
7975 #if HAVE_ASSUME_DEFAULT_COLORS
7976         if (assumed_colors)
7977             assume_default_colors(default_fg, default_bg);
7978 #endif
7979 #endif
7980 #else /* normal SVr4 curses */
7981         MaxColors = COLORS;     /* was > 8 ? 8 : COLORS */
7982 #endif
7983         max_pairs = COLOR_PAIRS;        /* was > 256 ? 256 : COLOR_PAIRS */
7984
7985 #if HAVE_COLOR_CONTENT
7986         if (can_change_color()) {
7987             init_all_colors(xterm_colors, palette_file);
7988         }
7989 #endif
7990     }
7991
7992     /*
7993      * Return to terminal mode, so we're guaranteed of being able to
7994      * select terminal commands even if the capabilities are wrong.
7995      */
7996     endwin();
7997
7998 #if HAVE_CURSES_VERSION
7999     (void) printf("Welcome to %s.  Press ? for help.\n", curses_version());
8000 #elif defined(NCURSES_VERSION_MAJOR) && defined(NCURSES_VERSION_MINOR) && defined(NCURSES_VERSION_PATCH)
8001     (void) printf("Welcome to ncurses %d.%d.%d.  Press ? for help.\n",
8002                   NCURSES_VERSION_MAJOR,
8003                   NCURSES_VERSION_MINOR,
8004                   NCURSES_VERSION_PATCH);
8005 #else
8006     (void) puts("Welcome to ncurses.  Press ? for help.");
8007 #endif
8008
8009     main_menu(TRUE);
8010
8011     ExitProgram(EXIT_SUCCESS);
8012 }
8013
8014 /* ncurses.c ends here */