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