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