ncurses 5.9 - patch 20140629
[ncurses.git] / test / ncurses.c
1 /****************************************************************************
2  * Copyright (c) 1998-2013,2014 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.408 2014/06/28 21:45:40 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 extern unsigned _nc_tracing;
81 #endif
82
83 #else
84
85 #define NCURSES_CONST_PARAM char
86
87 #define mmask_t chtype          /* not specified in XSI */
88
89 #ifndef ACS_S3
90 #ifdef CURSES_ACS_ARRAY
91 #define ACS_S3          (CURSES_ACS_ARRAY['p'])         /* scan line 3 */
92 #define ACS_S7          (CURSES_ACS_ARRAY['r'])         /* scan line 7 */
93 #define ACS_LEQUAL      (CURSES_ACS_ARRAY['y'])         /* less/equal */
94 #define ACS_GEQUAL      (CURSES_ACS_ARRAY['z'])         /* greater/equal */
95 #define ACS_PI          (CURSES_ACS_ARRAY['{'])         /* Pi */
96 #define ACS_NEQUAL      (CURSES_ACS_ARRAY['|'])         /* not equal */
97 #define ACS_STERLING    (CURSES_ACS_ARRAY['}'])         /* UK pound sign */
98 #else
99 #define ACS_S3          (A_ALTCHARSET + 'p')    /* scan line 3 */
100 #define ACS_S7          (A_ALTCHARSET + 'r')    /* scan line 7 */
101 #define ACS_LEQUAL      (A_ALTCHARSET + 'y')    /* less/equal */
102 #define ACS_GEQUAL      (A_ALTCHARSET + 'z')    /* greater/equal */
103 #define ACS_PI          (A_ALTCHARSET + '{')    /* Pi */
104 #define ACS_NEQUAL      (A_ALTCHARSET + '|')    /* not equal */
105 #define ACS_STERLING    (A_ALTCHARSET + '}')    /* UK pound sign */
106 #endif
107 #endif /* ACS_S3 */
108
109 #ifndef WACS_S3
110 #ifdef CURSES_WACS_ARRAY
111 #define WACS_S3         (&(CURSES_WACS_ARRAY['p']))     /* scan line 3 */
112 #define WACS_S7         (&(CURSES_WACS_ARRAY['r']))     /* scan line 7 */
113 #define WACS_LEQUAL     (&(CURSES_WACS_ARRAY['y']))     /* less/equal */
114 #define WACS_GEQUAL     (&(CURSES_WACS_ARRAY['z']))     /* greater/equal */
115 #define WACS_PI         (&(CURSES_WACS_ARRAY['{']))     /* Pi */
116 #define WACS_NEQUAL     (&(CURSES_WACS_ARRAY['|']))     /* not equal */
117 #define WACS_STERLING   (&(CURSES_WACS_ARRAY['}']))     /* UK pound sign */
118 #endif
119 #endif
120
121 #endif
122
123 #if HAVE_WCSRTOMBS
124 #define count_wchars(src, len, state)      wcsrtombs(0,   &src, len, state)
125 #define trans_wchars(dst, src, len, state) wcsrtombs(dst, &src, len, state)
126 #define reset_wchars(state) init_mb(state)
127 #elif HAVE_WCSTOMBS && HAVE_MBTOWC && HAVE_MBLEN
128 #define count_wchars(src, len, state)      wcstombs(0,   src, len)
129 #define trans_wchars(dst, src, len, state) wcstombs(dst, src, len)
130 #define reset_wchars(state) IGNORE_RC(mblen(NULL, 0)), IGNORE_RC(mbtowc(NULL, NULL, 0))
131 #define state_unused
132 #endif
133
134 #if HAVE_MBSRTOWCS
135 #define count_mbytes(src, len, state)      mbsrtowcs(0,   &src, len, state)
136 #define trans_mbytes(dst, src, len, state) mbsrtowcs(dst, &src, len, state)
137 #define reset_mbytes(state) init_mb(state)
138 #elif HAVE_MBSTOWCS && HAVE_MBTOWC && HAVE_MBLEN
139 #define count_mbytes(src, len, state)      mbstowcs(0,   src, len)
140 #define trans_mbytes(dst, src, len, state) mbstowcs(dst, src, len)
141 #define reset_mbytes(state) IGNORE_RC(mblen(NULL, 0)), IGNORE_RC(mbtowc(NULL, NULL, 0))
142 #define state_unused
143 #endif
144
145 #define ToggleAcs(temp,real) temp = ((temp == real) ? 0 : real)
146
147 #define P(string)       printw("%s\n", string)
148
149 #define BLANK           ' '     /* this is the background character */
150
151 #undef max_colors
152 static int max_colors;          /* the actual number of colors we'll use */
153 static int min_colors;          /* the minimum color code */
154 static bool use_colors;         /* true if we use colors */
155
156 #undef max_pairs
157 static int max_pairs;           /* ...and the number of color pairs */
158
159 typedef struct {
160     NCURSES_COLOR_T red;
161     NCURSES_COLOR_T green;
162     NCURSES_COLOR_T blue;
163 } RGB_DATA;
164
165 static RGB_DATA *all_colors;
166
167 static void main_menu(bool);
168
169 static void
170 failed(const char *s)
171 {
172     perror(s);
173     endwin();
174     ExitProgram(EXIT_FAILURE);
175 }
176
177 /* The behavior of mvhline, mvvline for negative/zero length is unspecified,
178  * though we can rely on negative x/y values to stop the macro.
179  */
180 static void
181 do_h_line(int y, int x, chtype c, int to)
182 {
183     if ((to) > (x))
184         MvHLine(y, x, c, (to) - (x));
185 }
186
187 static void
188 do_v_line(int y, int x, chtype c, int to)
189 {
190     if ((to) > (y))
191         MvVLine(y, x, c, (to) - (y));
192 }
193
194 static void
195 Repaint(void)
196 {
197     touchwin(stdscr);
198     touchwin(curscr);
199     wrefresh(curscr);
200 }
201
202 static bool
203 isQuit(int c)
204 {
205     return ((c) == QUIT || (c) == ESCAPE);
206 }
207 #define case_QUIT       QUIT: case ESCAPE
208
209 /* Common function to allow ^T to toggle trace-mode in the middle of a test
210  * so that trace-files can be made smaller.
211  */
212 static int
213 wGetchar(WINDOW *win)
214 {
215     int c;
216 #ifdef TRACE
217     while ((c = wgetch(win)) == CTRL('T')) {
218         if (_nc_tracing) {
219             save_trace = _nc_tracing;
220             Trace(("TOGGLE-TRACING OFF"));
221             _nc_tracing = 0;
222         } else {
223             _nc_tracing = save_trace;
224         }
225         trace(_nc_tracing);
226         if (_nc_tracing)
227             Trace(("TOGGLE-TRACING ON"));
228     }
229 #else
230     c = wgetch(win);
231 #endif
232     return c;
233 }
234 #define Getchar() wGetchar(stdscr)
235
236 /* replaces wgetnstr(), since we want to be able to edit values */
237 static void
238 wGetstring(WINDOW *win, char *buffer, int limit)
239 {
240     int y0, x0, x, ch;
241     bool done = FALSE;
242
243     echo();
244     getyx(win, y0, x0);
245     (void) wattrset(win, A_REVERSE);
246
247     x = (int) strlen(buffer);
248     while (!done) {
249         if (x > (int) strlen(buffer))
250             x = (int) strlen(buffer);
251         wmove(win, y0, x0);
252         wprintw(win, "%-*s", limit, buffer);
253         wmove(win, y0, x0 + x);
254         switch (ch = wGetchar(win)) {
255         case '\n':
256         case KEY_ENTER:
257             done = TRUE;
258             break;
259         case CTRL('U'):
260             *buffer = '\0';
261             break;
262         case '\b':
263         case KEY_BACKSPACE:
264         case KEY_DC:
265             if (x > 0) {
266                 int j;
267                 for (j = --x; (buffer[j] = buffer[j + 1]) != '\0'; ++j) {
268                     ;
269                 }
270             } else {
271                 beep();
272             }
273             break;
274         case KEY_LEFT:
275             if (x > 0) {
276                 --x;
277             } else {
278                 flash();
279             }
280             break;
281         case KEY_RIGHT:
282             ++x;
283             break;
284         default:
285             if (!isprint(ch) || ch >= KEY_MIN) {
286                 beep();
287             } else if ((int) strlen(buffer) < limit) {
288                 int j;
289                 for (j = (int) strlen(buffer) + 1; j > x; --j) {
290                     buffer[j] = buffer[j - 1];
291                 }
292                 buffer[x++] = (char) ch;
293             } else {
294                 flash();
295             }
296         }
297     }
298
299     wattroff(win, A_REVERSE);
300     wmove(win, y0, x0);
301     noecho();
302 }
303
304 #if USE_WIDEC_SUPPORT
305 static wchar_t
306 fullwidth_digit(int ch)
307 {
308     return (ch + 0xff10 - '0');
309 }
310
311 static void
312 make_fullwidth_text(wchar_t *target, const char *source)
313 {
314     int ch;
315     while ((ch = *source++) != 0) {
316         *target++ = fullwidth_digit(ch);
317     }
318     *target = 0;
319 }
320
321 static void
322 make_narrow_text(wchar_t *target, const char *source)
323 {
324     int ch;
325     while ((ch = *source++) != 0) {
326         *target++ = ch;
327     }
328     *target = 0;
329 }
330
331 #if USE_LIBPANEL
332 static void
333 make_fullwidth_digit(cchar_t *target, int digit)
334 {
335     wchar_t source[2];
336
337     source[0] = fullwidth_digit(digit + '0');
338     source[1] = 0;
339     setcchar(target, source, A_NORMAL, 0, 0);
340 }
341 #endif
342
343 static int
344 wGet_wchar(WINDOW *win, wint_t *result)
345 {
346     int c;
347 #ifdef TRACE
348     while ((c = wget_wch(win, result)) == CTRL('T')) {
349         if (_nc_tracing) {
350             save_trace = _nc_tracing;
351             Trace(("TOGGLE-TRACING OFF"));
352             _nc_tracing = 0;
353         } else {
354             _nc_tracing = save_trace;
355         }
356         trace(_nc_tracing);
357         if (_nc_tracing)
358             Trace(("TOGGLE-TRACING ON"));
359     }
360 #else
361     c = wget_wch(win, result);
362 #endif
363     return c;
364 }
365 #define Get_wchar(result) wGet_wchar(stdscr, result)
366
367 /* replaces wgetn_wstr(), since we want to be able to edit values */
368 static void
369 wGet_wstring(WINDOW *win, wchar_t *buffer, int limit)
370 {
371     int y0, x0, x;
372     wint_t ch;
373     bool done = FALSE;
374     bool fkey = FALSE;
375
376     echo();
377     getyx(win, y0, x0);
378     (void) wattrset(win, A_REVERSE);
379
380     x = (int) wcslen(buffer);
381     while (!done) {
382         if (x > (int) wcslen(buffer))
383             x = (int) wcslen(buffer);
384
385         /* clear the "window' */
386         wmove(win, y0, x0);
387         wprintw(win, "%*s", limit, " ");
388
389         /* write the existing buffer contents */
390         wmove(win, y0, x0);
391         waddnwstr(win, buffer, limit);
392
393         /* positions the cursor past character 'x' */
394         wmove(win, y0, x0);
395         waddnwstr(win, buffer, x);
396
397         switch (wGet_wchar(win, &ch)) {
398         case KEY_CODE_YES:
399             fkey = TRUE;
400             switch (ch) {
401             case KEY_ENTER:
402                 ch = '\n';
403                 fkey = FALSE;
404                 break;
405             case KEY_BACKSPACE:
406             case KEY_DC:
407                 ch = '\b';
408                 fkey = FALSE;
409                 break;
410             case KEY_LEFT:
411             case KEY_RIGHT:
412                 break;
413             default:
414                 ch = (wint_t) -1;
415                 break;
416             }
417             break;
418         case OK:
419             fkey = FALSE;
420             break;
421         default:
422             ch = (wint_t) -1;
423             fkey = TRUE;
424             break;
425         }
426
427         switch (ch) {
428         case '\n':
429             done = TRUE;
430             break;
431         case CTRL('U'):
432             *buffer = '\0';
433             break;
434         case '\b':
435             if (x > 0) {
436                 int j;
437                 for (j = --x; (buffer[j] = buffer[j + 1]) != '\0'; ++j) {
438                     ;
439                 }
440             } else {
441                 beep();
442             }
443             break;
444         case KEY_LEFT:
445             if (x > 0) {
446                 --x;
447             } else {
448                 beep();
449             }
450             break;
451         case KEY_RIGHT:
452             ++x;
453             break;
454         default:
455             if (fkey) {
456                 beep();
457             } else if ((int) wcslen(buffer) < limit) {
458                 int j;
459                 for (j = (int) wcslen(buffer) + 1; j > x; --j) {
460                     buffer[j] = buffer[j - 1];
461                 }
462                 buffer[x++] = (wchar_t) ch;
463             } else {
464                 beep();
465             }
466         }
467     }
468
469     wattroff(win, A_REVERSE);
470     wmove(win, y0, x0);
471     noecho();
472 }
473
474 #endif
475
476 static void
477 Pause(void)
478 {
479     move(LINES - 1, 0);
480     addstr("Press any key to continue... ");
481     (void) Getchar();
482 }
483
484 static void
485 Cannot(const char *what)
486 {
487     printw("\nThis %s terminal %s\n\n", getenv("TERM"), what);
488     Pause();
489 }
490
491 static void
492 ShellOut(bool message)
493 {
494     if (message)
495         addstr("Shelling out...");
496     def_prog_mode();
497     endwin();
498 #ifdef __MINGW32__
499     system("cmd.exe");
500 #else
501     IGNORE_RC(system("sh"));
502 #endif
503     if (message)
504         addstr("returned from shellout.\n");
505     refresh();
506 }
507
508 #ifdef NCURSES_MOUSE_VERSION
509 /*
510  * This function is the same as _tracemouse(), but we cannot count on that
511  * being available in the non-debug library.
512  */
513 static const char *
514 mouse_decode(MEVENT const *ep)
515 {
516     static char buf[80 + (5 * 10) + (32 * 15)];
517
518     (void) sprintf(buf, "id %2d at (%2d, %2d, %d) state %4lx = {",
519                    ep->id, ep->x, ep->y, ep->z, (unsigned long) ep->bstate);
520
521 #define SHOW(m, s) if ((ep->bstate & m)==m) {strcat(buf,s); strcat(buf, ", ");}
522
523     SHOW(BUTTON1_RELEASED, "release-1");
524     SHOW(BUTTON1_PRESSED, "press-1");
525     SHOW(BUTTON1_CLICKED, "click-1");
526     SHOW(BUTTON1_DOUBLE_CLICKED, "doubleclick-1");
527     SHOW(BUTTON1_TRIPLE_CLICKED, "tripleclick-1");
528 #if NCURSES_MOUSE_VERSION == 1
529     SHOW(BUTTON1_RESERVED_EVENT, "reserved-1");
530 #endif
531
532     SHOW(BUTTON2_RELEASED, "release-2");
533     SHOW(BUTTON2_PRESSED, "press-2");
534     SHOW(BUTTON2_CLICKED, "click-2");
535     SHOW(BUTTON2_DOUBLE_CLICKED, "doubleclick-2");
536     SHOW(BUTTON2_TRIPLE_CLICKED, "tripleclick-2");
537 #if NCURSES_MOUSE_VERSION == 1
538     SHOW(BUTTON2_RESERVED_EVENT, "reserved-2");
539 #endif
540
541     SHOW(BUTTON3_RELEASED, "release-3");
542     SHOW(BUTTON3_PRESSED, "press-3");
543     SHOW(BUTTON3_CLICKED, "click-3");
544     SHOW(BUTTON3_DOUBLE_CLICKED, "doubleclick-3");
545     SHOW(BUTTON3_TRIPLE_CLICKED, "tripleclick-3");
546 #if NCURSES_MOUSE_VERSION == 1
547     SHOW(BUTTON3_RESERVED_EVENT, "reserved-3");
548 #endif
549
550     SHOW(BUTTON4_RELEASED, "release-4");
551     SHOW(BUTTON4_PRESSED, "press-4");
552     SHOW(BUTTON4_CLICKED, "click-4");
553     SHOW(BUTTON4_DOUBLE_CLICKED, "doubleclick-4");
554     SHOW(BUTTON4_TRIPLE_CLICKED, "tripleclick-4");
555 #if NCURSES_MOUSE_VERSION == 1
556     SHOW(BUTTON4_RESERVED_EVENT, "reserved-4");
557 #endif
558
559 #if NCURSES_MOUSE_VERSION == 2
560     SHOW(BUTTON5_RELEASED, "release-5");
561     SHOW(BUTTON5_PRESSED, "press-5");
562     SHOW(BUTTON5_CLICKED, "click-5");
563     SHOW(BUTTON5_DOUBLE_CLICKED, "doubleclick-5");
564     SHOW(BUTTON5_TRIPLE_CLICKED, "tripleclick-5");
565 #endif
566
567     SHOW(BUTTON_CTRL, "ctrl");
568     SHOW(BUTTON_SHIFT, "shift");
569     SHOW(BUTTON_ALT, "alt");
570     SHOW(ALL_MOUSE_EVENTS, "all-events");
571     SHOW(REPORT_MOUSE_POSITION, "position");
572
573 #undef SHOW
574
575     if (buf[strlen(buf) - 1] == ' ')
576         buf[strlen(buf) - 2] = '\0';
577     (void) strcat(buf, "}");
578     return (buf);
579 }
580
581 static void
582 show_mouse(WINDOW *win)
583 {
584     int y, x;
585     MEVENT event;
586     bool outside;
587     bool show_loc;
588
589     getmouse(&event);
590     outside = !wenclose(win, event.y, event.x);
591
592     if (outside) {
593         (void) wstandout(win);
594         waddstr(win, "KEY_MOUSE");
595         (void) wstandend(win);
596     } else {
597         waddstr(win, "KEY_MOUSE");
598     }
599     wprintw(win, ", %s", mouse_decode(&event));
600
601     if (outside)
602         win = stdscr;
603
604     show_loc = wmouse_trafo(win, &event.y, &event.x, FALSE);
605
606     if (show_loc) {
607         getyx(win, y, x);
608         wmove(win, event.y, event.x);
609         waddch(win, '*');
610         wmove(win, y, x);
611     }
612
613     if (outside)
614         wnoutrefresh(win);
615 }
616 #endif /* NCURSES_MOUSE_VERSION */
617
618 /****************************************************************************
619  *
620  * Character input test
621  *
622  ****************************************************************************/
623
624 #define NUM_GETCH_FLAGS 256
625 typedef bool GetchFlags[NUM_GETCH_FLAGS];
626
627 static void
628 setup_getch(WINDOW *win, GetchFlags flags)
629 {
630     keypad(win, flags['k']);    /* should be redundant, but for testing */
631     meta(win, flags['m']);      /* force this to a known state */
632     if (flags['e'])
633         echo();
634     else
635         noecho();
636 }
637
638 static void
639 init_getch(WINDOW *win, GetchFlags flags)
640 {
641     memset(flags, FALSE, NUM_GETCH_FLAGS);
642     flags[UChar('k')] = (win == stdscr);
643     flags[UChar('m')] = TRUE;
644
645     setup_getch(win, flags);
646 }
647
648 static void
649 wgetch_help(WINDOW *win, GetchFlags flags)
650 {
651     static const char *help[] =
652     {
653         "e  -- toggle echo mode"
654         ,"g  -- triggers a getstr test"
655         ,"k  -- toggle keypad/literal mode"
656         ,"m  -- toggle meta (7-bit/8-bit) mode"
657         ,"^q -- quit"
658         ,"s  -- shell out\n"
659         ,"w  -- create a new window"
660 #ifdef SIGTSTP
661         ,"z  -- suspend this process"
662 #endif
663     };
664     int y, x;
665     unsigned chk = ((SIZEOF(help) + 1) / 2);
666     unsigned n;
667
668     getyx(win, y, x);
669     move(0, 0);
670     printw("Type any key to see its %s value.  Also:\n",
671            flags['k'] ? "keypad" : "literal");
672     for (n = 0; n < SIZEOF(help); ++n) {
673         int row = 1 + (int) (n % chk);
674         int col = (n >= chk) ? COLS / 2 : 0;
675         int flg = ((strstr(help[n], "toggle") != 0)
676                    && (flags[UChar(*help[n])] != FALSE));
677         if (flg)
678             (void) standout();
679         MvPrintw(row, col, "%s", help[n]);
680         if (col == 0)
681             clrtoeol();
682         if (flg)
683             (void) standend();
684     }
685     wrefresh(stdscr);
686     wmove(win, y, x);
687 }
688
689 static void
690 wgetch_wrap(WINDOW *win, int first_y)
691 {
692     int last_y = getmaxy(win) - 1;
693     int y = getcury(win) + 1;
694
695     if (y >= last_y)
696         y = first_y;
697     wmove(win, y, 0);
698     wclrtoeol(win);
699 }
700
701 #if defined(KEY_RESIZE) && HAVE_WRESIZE
702 typedef struct {
703     WINDOW *text;
704     WINDOW *frame;
705 } WINSTACK;
706
707 static WINSTACK *winstack = 0;
708 static unsigned len_winstack = 0;
709
710 static void
711 forget_boxes(void)
712 {
713     if (winstack != 0) {
714         free(winstack);
715     }
716     winstack = 0;
717     len_winstack = 0;
718 }
719
720 static void
721 remember_boxes(unsigned level, WINDOW *txt_win, WINDOW *box_win)
722 {
723     unsigned need = (level + 1) * 2;
724
725     assert(level < (unsigned) COLS);
726
727     if (winstack == 0) {
728         len_winstack = 20;
729         winstack = typeMalloc(WINSTACK, len_winstack);
730     } else if (need >= len_winstack) {
731         len_winstack = need;
732         winstack = typeRealloc(WINSTACK, len_winstack, winstack);
733     }
734     if (!winstack)
735         failed("remember_boxes");
736     winstack[level].text = txt_win;
737     winstack[level].frame = box_win;
738 }
739
740 #if USE_SOFTKEYS && (defined(NCURSES_VERSION_PATCH) && NCURSES_VERSION_PATCH < 20071229) && NCURSES_EXT_FUNCS
741 static void
742 slk_repaint(void)
743 {
744     /* this chunk is now done in resize_term() */
745     slk_touch();
746     slk_clear();
747     slk_noutrefresh();
748 }
749
750 #else
751 #define slk_repaint()           /* nothing */
752 #endif
753
754 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
755 /*
756  * For wgetch_test(), we create pairs of windows - one for a box, one for text.
757  * Resize both and paint the box in the parent.
758  */
759 static void
760 resize_boxes(unsigned level, WINDOW *win)
761 {
762     unsigned n;
763     int base = 5;
764     int high = LINES - base;
765     int wide = COLS;
766
767     touchwin(stdscr);
768     wnoutrefresh(stdscr);
769
770     slk_repaint();
771
772     for (n = 0; n < level; ++n) {
773         wresize(winstack[n].frame, high, wide);
774         wresize(winstack[n].text, high - 2, wide - 2);
775         high -= 2;
776         wide -= 2;
777         werase(winstack[n].text);
778         box(winstack[n].frame, 0, 0);
779         wnoutrefresh(winstack[n].frame);
780         wprintw(winstack[n].text,
781                 "size %dx%d\n",
782                 getmaxy(winstack[n].text),
783                 getmaxx(winstack[n].text));
784         wnoutrefresh(winstack[n].text);
785         if (winstack[n].text == win)
786             break;
787     }
788     doupdate();
789 }
790 #endif /* resize_boxes */
791 #else
792 #define forget_boxes()          /* nothing */
793 #define remember_boxes(level,text,frame)        /* nothing */
794 #endif
795
796 /*
797  * Return-code is OK/ERR or a keyname.
798  */
799 static const char *
800 ok_keyname(int code)
801 {
802     return ((code == OK) ? "OK" : ((code == ERR) ? "ERR" : keyname(code)));
803 }
804
805 static void
806 wgetch_test(unsigned level, WINDOW *win, int delay)
807 {
808     char buf[BUFSIZ];
809     int first_y, first_x;
810     int c;
811     int incount = 0;
812     GetchFlags flags;
813     bool blocking = (delay < 0);
814
815     init_getch(win, flags);
816     wtimeout(win, delay);
817     getyx(win, first_y, first_x);
818
819     wgetch_help(win, flags);
820     wsetscrreg(win, first_y, getmaxy(win) - 1);
821     scrollok(win, TRUE);
822
823     for (;;) {
824         while ((c = wGetchar(win)) == ERR) {
825             incount++;
826             if (blocking) {
827                 (void) wprintw(win, "%05d: input error", incount);
828                 break;
829             } else {
830                 (void) wprintw(win, "%05d: input timed out", incount);
831             }
832             wgetch_wrap(win, first_y);
833         }
834         if (c == ERR && blocking) {
835             wprintw(win, "ERR");
836             wgetch_wrap(win, first_y);
837         } else if (isQuit(c)) {
838             break;
839         } else if (c == 'e') {
840             flags[UChar('e')] = !flags[UChar('e')];
841             setup_getch(win, flags);
842             wgetch_help(win, flags);
843         } else if (c == 'g') {
844             waddstr(win, "getstr test: ");
845             echo();
846             c = wgetnstr(win, buf, sizeof(buf) - 1);
847             noecho();
848             wprintw(win, "I saw %d characters:\n\t`%s' (%s).",
849                     (int) strlen(buf), buf,
850                     ok_keyname(c));
851             wclrtoeol(win);
852             wgetch_wrap(win, first_y);
853         } else if (c == 'k') {
854             flags[UChar('k')] = !flags[UChar('k')];
855             setup_getch(win, flags);
856             wgetch_help(win, flags);
857         } else if (c == 'm') {
858             flags[UChar('m')] = !flags[UChar('m')];
859             setup_getch(win, flags);
860             wgetch_help(win, flags);
861         } else if (c == 's') {
862             ShellOut(TRUE);
863         } else if (c == 'w') {
864             int high = getmaxy(win) - 1 - first_y + 1;
865             int wide = getmaxx(win) - first_x;
866             int old_y, old_x;
867             int new_y = first_y + getbegy(win);
868             int new_x = first_x + getbegx(win);
869
870             getyx(win, old_y, old_x);
871             if (high > 2 && wide > 2) {
872                 WINDOW *wb = newwin(high, wide, new_y, new_x);
873                 WINDOW *wi = newwin(high - 2, wide - 2, new_y + 1, new_x + 1);
874
875                 box(wb, 0, 0);
876                 wrefresh(wb);
877                 wmove(wi, 0, 0);
878                 remember_boxes(level, wi, wb);
879                 wgetch_test(level + 1, wi, delay);
880                 delwin(wi);
881                 delwin(wb);
882
883                 wgetch_help(win, flags);
884                 wmove(win, old_y, old_x);
885                 touchwin(win);
886                 wrefresh(win);
887                 doupdate();
888             }
889 #ifdef SIGTSTP
890         } else if (c == 'z') {
891             kill(getpid(), SIGTSTP);
892 #endif
893         } else {
894             wprintw(win, "Key pressed: %04o ", c);
895 #ifdef NCURSES_MOUSE_VERSION
896             if (c == KEY_MOUSE) {
897                 show_mouse(win);
898             } else
899 #endif /* NCURSES_MOUSE_VERSION */
900             if (c >= KEY_MIN) {
901 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
902                 if (c == KEY_RESIZE) {
903                     resize_boxes(level, win);
904                 }
905 #endif
906                 (void) waddstr(win, keyname(c));
907             } else if (c >= 0x80) {
908                 unsigned c2 = (unsigned) c;
909 #if !(defined(NCURSES_VERSION) || defined(_XOPEN_CURSES))
910                 /* at least Solaris SVR4 curses breaks unctrl(128), etc. */
911                 c2 &= 0x7f;
912 #endif
913                 if (isprint(c))
914                     (void) wprintw(win, "%c", UChar(c));
915                 else if (c2 != UChar(c))
916                     (void) wprintw(win, "M-%s", unctrl(c2));
917                 else
918                     (void) wprintw(win, "%s", unctrl(c2));
919                 waddstr(win, " (high-half character)");
920             } else {
921                 if (isprint(c))
922                     (void) wprintw(win, "%c (ASCII printable character)", c);
923                 else
924                     (void) wprintw(win, "%s (ASCII control character)",
925                                    unctrl(UChar(c)));
926             }
927             wgetch_wrap(win, first_y);
928         }
929     }
930
931     wtimeout(win, -1);
932
933     if (!level)
934         init_getch(win, flags);
935 }
936
937 static int
938 begin_getch_test(void)
939 {
940     char buf[BUFSIZ];
941     int delay;
942
943     refresh();
944
945 #ifdef NCURSES_MOUSE_VERSION
946     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
947 #endif
948
949     (void) printw("Delay in 10ths of a second (<CR> for blocking input)? ");
950     echo();
951     getnstr(buf, sizeof(buf) - 1);
952     noecho();
953     nonl();
954
955     if (isdigit(UChar(buf[0]))) {
956         delay = atoi(buf) * 100;
957     } else {
958         delay = -1;
959     }
960     raw();
961     move(5, 0);
962     return delay;
963 }
964
965 static void
966 finish_getch_test(void)
967 {
968 #ifdef NCURSES_MOUSE_VERSION
969     mousemask(0, (mmask_t *) 0);
970 #endif
971     erase();
972     noraw();
973     nl();
974     endwin();
975 }
976
977 static void
978 getch_test(void)
979 {
980     int delay = begin_getch_test();
981
982     slk_restore();
983     wgetch_test(0, stdscr, delay);
984     forget_boxes();
985     finish_getch_test();
986     slk_clear();
987 }
988
989 #if USE_WIDEC_SUPPORT
990 /*
991  * For wget_wch_test(), we create pairs of windows - one for a box, one for text.
992  * Resize both and paint the box in the parent.
993  */
994 #if defined(KEY_RESIZE) && HAVE_WRESIZE
995 static void
996 resize_wide_boxes(unsigned level, WINDOW *win)
997 {
998     unsigned n;
999     int base = 5;
1000     int high = LINES - base;
1001     int wide = COLS;
1002
1003     touchwin(stdscr);
1004     wnoutrefresh(stdscr);
1005
1006     slk_repaint();
1007
1008     for (n = 0; n < level; ++n) {
1009         wresize(winstack[n].frame, high, wide);
1010         wresize(winstack[n].text, high - 2, wide - 2);
1011         high -= 2;
1012         wide -= 2;
1013         werase(winstack[n].text);
1014         box_set(winstack[n].frame, 0, 0);
1015         wnoutrefresh(winstack[n].frame);
1016         wprintw(winstack[n].text,
1017                 "size %dx%d\n",
1018                 getmaxy(winstack[n].text),
1019                 getmaxx(winstack[n].text));
1020         wnoutrefresh(winstack[n].text);
1021         if (winstack[n].text == win)
1022             break;
1023     }
1024     doupdate();
1025 }
1026 #endif /* KEY_RESIZE */
1027
1028 static char *
1029 wcstos(const wchar_t *src)
1030 {
1031     int need;
1032     char *result = 0;
1033     const wchar_t *tmp = src;
1034 #ifndef state_unused
1035     mbstate_t state;
1036 #endif
1037
1038     reset_wchars(state);
1039     if ((need = (int) count_wchars(tmp, 0, &state)) > 0) {
1040         unsigned have = (unsigned) need;
1041         if ((result = typeCalloc(char, have + 1)) != 0) {
1042             tmp = src;
1043             if (trans_wchars(result, tmp, have, &state) != have) {
1044                 free(result);
1045                 result = 0;
1046             }
1047         } else {
1048             failed("wcstos");
1049         }
1050     }
1051     return result;
1052 }
1053
1054 static void
1055 wget_wch_test(unsigned level, WINDOW *win, int delay)
1056 {
1057     wchar_t wchar_buf[BUFSIZ];
1058     wint_t wint_buf[BUFSIZ];
1059     int first_y, first_x;
1060     wint_t c;
1061     int incount = 0;
1062     GetchFlags flags;
1063     bool blocking = (delay < 0);
1064     int code;
1065     char *temp;
1066
1067     init_getch(win, flags);
1068     wtimeout(win, delay);
1069     getyx(win, first_y, first_x);
1070
1071     wgetch_help(win, flags);
1072     wsetscrreg(win, first_y, getmaxy(win) - 1);
1073     scrollok(win, TRUE);
1074
1075     for (;;) {
1076         while ((code = wGet_wchar(win, &c)) == ERR) {
1077             incount++;
1078             if (blocking) {
1079                 (void) wprintw(win, "%05d: input error", incount);
1080                 break;
1081             } else {
1082                 (void) wprintw(win, "%05d: input timed out", incount);
1083             }
1084             wgetch_wrap(win, first_y);
1085         }
1086         if (code == ERR && blocking) {
1087             wprintw(win, "ERR");
1088             wgetch_wrap(win, first_y);
1089         } else if (isQuit((int) c)) {
1090             break;
1091         } else if (c == 'e') {
1092             flags[UChar('e')] = !flags[UChar('e')];
1093             setup_getch(win, flags);
1094             wgetch_help(win, flags);
1095         } else if (c == 'g') {
1096             waddstr(win, "getstr test: ");
1097             echo();
1098             code = wgetn_wstr(win, wint_buf, BUFSIZ - 1);
1099             noecho();
1100             if (code == ERR) {
1101                 wprintw(win, "wgetn_wstr returns an error.");
1102             } else {
1103                 int n;
1104                 for (n = 0; (wchar_buf[n] = (wchar_t) wint_buf[n]) != 0; ++n) {
1105                     ;
1106                 }
1107                 if ((temp = wcstos(wchar_buf)) != 0) {
1108                     wprintw(win, "I saw %d characters:\n\t`%s'.",
1109                             (int) wcslen(wchar_buf), temp);
1110                     free(temp);
1111                 } else {
1112                     wprintw(win, "I saw %d characters (cannot convert).",
1113                             (int) wcslen(wchar_buf));
1114                 }
1115             }
1116             wclrtoeol(win);
1117             wgetch_wrap(win, first_y);
1118         } else if (c == 'k') {
1119             flags[UChar('k')] = !flags[UChar('k')];
1120             setup_getch(win, flags);
1121             wgetch_help(win, flags);
1122         } else if (c == 'm') {
1123             flags[UChar('m')] = !flags[UChar('m')];
1124             setup_getch(win, flags);
1125             wgetch_help(win, flags);
1126         } else if (c == 's') {
1127             ShellOut(TRUE);
1128         } else if (c == 'w') {
1129             int high = getmaxy(win) - 1 - first_y + 1;
1130             int wide = getmaxx(win) - first_x;
1131             int old_y, old_x;
1132             int new_y = first_y + getbegy(win);
1133             int new_x = first_x + getbegx(win);
1134
1135             getyx(win, old_y, old_x);
1136             if (high > 2 && wide > 2) {
1137                 WINDOW *wb = newwin(high, wide, new_y, new_x);
1138                 WINDOW *wi = newwin(high - 2, wide - 2, new_y + 1, new_x + 1);
1139
1140                 box_set(wb, 0, 0);
1141                 wrefresh(wb);
1142                 wmove(wi, 0, 0);
1143                 remember_boxes(level, wi, wb);
1144                 wget_wch_test(level + 1, wi, delay);
1145                 delwin(wi);
1146                 delwin(wb);
1147
1148                 wgetch_help(win, flags);
1149                 wmove(win, old_y, old_x);
1150                 touchwin(win);
1151                 wrefresh(win);
1152             }
1153 #ifdef SIGTSTP
1154         } else if (c == 'z') {
1155             kill(getpid(), SIGTSTP);
1156 #endif
1157         } else {
1158             wprintw(win, "Key pressed: %04o ", (int) c);
1159 #ifdef NCURSES_MOUSE_VERSION
1160             if (c == KEY_MOUSE) {
1161                 show_mouse(win);
1162             } else
1163 #endif /* NCURSES_MOUSE_VERSION */
1164             if (code == KEY_CODE_YES) {
1165 #if defined(KEY_RESIZE) && HAVE_WRESIZE
1166                 if (c == KEY_RESIZE) {
1167                     resize_wide_boxes(level, win);
1168                 }
1169 #endif
1170                 (void) waddstr(win, keyname((wchar_t) c));
1171             } else {
1172                 (void) waddstr(win, key_name((wchar_t) c));
1173                 if (c < 256 && iscntrl(c)) {
1174                     (void) wprintw(win, " (control character)");
1175                 } else {
1176                     (void) wprintw(win, " = %#x (printable character)",
1177                                    (unsigned) c);
1178                 }
1179             }
1180             wgetch_wrap(win, first_y);
1181         }
1182     }
1183
1184     wtimeout(win, -1);
1185
1186     if (!level)
1187         init_getch(win, flags);
1188 }
1189
1190 static void
1191 get_wch_test(void)
1192 {
1193     int delay = begin_getch_test();
1194
1195     slk_restore();
1196     wget_wch_test(0, stdscr, delay);
1197     forget_boxes();
1198     finish_getch_test();
1199     slk_clear();
1200 }
1201 #endif
1202
1203 /****************************************************************************
1204  *
1205  * Character attributes test
1206  *
1207  ****************************************************************************/
1208
1209 #if HAVE_SETUPTERM || HAVE_TGETENT
1210 #define get_ncv() TIGETNUM("ncv","NC")
1211 #define get_xmc() TIGETNUM("xmc","sg")
1212 #else
1213 #define get_ncv() -1
1214 #define get_xmc() -1
1215 #endif
1216
1217 #if !HAVE_TERMATTRS
1218 static chtype
1219 my_termattrs(void)
1220 {
1221     static int first = TRUE;
1222     static chtype result = 0;
1223
1224     if (first) {
1225 #if !HAVE_TIGETSTR
1226         char buffer[4096];
1227         char parsed[4096];
1228         char *area_pointer = parsed;
1229
1230         tgetent(buffer, getenv("TERM"));
1231 #endif
1232
1233         if (TIGETSTR("smso", "so"))
1234             result |= A_STANDOUT;
1235         if (TIGETSTR("smul", "us"))
1236             result |= A_UNDERLINE;
1237         if (TIGETSTR("rev", "mr"))
1238             result |= A_REVERSE;
1239         if (TIGETSTR("blink", "mb"))
1240             result |= A_BLINK;
1241         if (TIGETSTR("dim", "mh"))
1242             result |= A_DIM;
1243         if (TIGETSTR("bold", "md"))
1244             result |= A_BOLD;
1245         if (TIGETSTR("smacs", "ac"))
1246             result |= A_ALTCHARSET;
1247
1248         first = FALSE;
1249     }
1250     return result;
1251 }
1252 #define termattrs() my_termattrs()
1253 #endif
1254
1255 #define ATTRSTRING_1ST 32       /* ' ' */
1256 #define ATTRSTRING_END 126      /* '~' */
1257
1258 #define COL_ATTRSTRING 25
1259 #define MARGIN_4_ATTRS (COL_ATTRSTRING + 8)
1260 #define LEN_ATTRSTRING (COLS - MARGIN_4_ATTRS)
1261 #define MAX_ATTRSTRING (ATTRSTRING_END + 1 - ATTRSTRING_1ST)
1262
1263 static char attr_test_string[MAX_ATTRSTRING + 1];
1264
1265 static void
1266 attr_legend(WINDOW *helpwin)
1267 {
1268     int row = 1;
1269     int col = 1;
1270
1271     MvWPrintw(helpwin, row++, col,
1272               "ESC to exit.");
1273     MvWPrintw(helpwin, row++, col,
1274               "^L repaints.");
1275     ++row;
1276     MvWPrintw(helpwin, row++, col,
1277               "Modify the test strings:");
1278     MvWPrintw(helpwin, row++, col,
1279               "  A digit sets gaps on each side of displayed attributes");
1280     MvWPrintw(helpwin, row++, col,
1281               "  </> shifts the text left/right. ");
1282     ++row;
1283     MvWPrintw(helpwin, row++, col,
1284               "Toggles:");
1285     if (use_colors) {
1286         MvWPrintw(helpwin, row++, col,
1287                   "  f/F/b/F toggle foreground/background background color");
1288         MvWPrintw(helpwin, row++, col,
1289                   "  t/T     toggle text/background color attribute");
1290     }
1291     MvWPrintw(helpwin, row++, col,
1292               "  a/A     toggle ACS (alternate character set) mapping");
1293     MvWPrintw(helpwin, row, col,
1294               "  v/V     toggle video attribute to combine with each line");
1295 }
1296
1297 static void
1298 show_color_attr(int fg, int bg, int tx)
1299 {
1300     if (use_colors) {
1301         printw("  Colors (fg %d, bg %d", fg, bg);
1302         if (tx >= 0)
1303             printw(", text %d", tx);
1304         printw("),");
1305     }
1306 }
1307
1308 static bool
1309 cycle_color_attr(int ch, NCURSES_COLOR_T *fg, NCURSES_COLOR_T *bg, NCURSES_COLOR_T *tx)
1310 {
1311     bool error = FALSE;
1312
1313     if (use_colors) {
1314         switch (ch) {
1315         case 'f':
1316             *fg = (NCURSES_COLOR_T) (*fg + 1);
1317             break;
1318         case 'F':
1319             *fg = (NCURSES_COLOR_T) (*fg - 1);
1320             break;
1321         case 'b':
1322             *bg = (NCURSES_COLOR_T) (*bg + 1);
1323             break;
1324         case 'B':
1325             *bg = (NCURSES_COLOR_T) (*bg - 1);
1326             break;
1327         case 't':
1328             *tx = (NCURSES_COLOR_T) (*tx + 1);
1329             break;
1330         case 'T':
1331             *tx = (NCURSES_COLOR_T) (*tx - 1);
1332             break;
1333         default:
1334             beep();
1335             error = TRUE;
1336             break;
1337         }
1338         if (*fg >= COLORS)
1339             *fg = (NCURSES_COLOR_T) min_colors;
1340         if (*fg < min_colors)
1341             *fg = (NCURSES_COLOR_T) (COLORS - 1);
1342         if (*bg >= COLORS)
1343             *bg = (NCURSES_COLOR_T) min_colors;
1344         if (*bg < min_colors)
1345             *bg = (NCURSES_COLOR_T) (COLORS - 1);
1346         if (*tx >= COLORS)
1347             *tx = -1;
1348         if (*tx < -1)
1349             *tx = (NCURSES_COLOR_T) (COLORS - 1);
1350     } else {
1351         beep();
1352         error = TRUE;
1353     }
1354     return error;
1355 }
1356
1357 static void
1358 adjust_attr_string(int adjust)
1359 {
1360     char save = attr_test_string[0];
1361     int first = ((int) UChar(save)) + adjust;
1362     int j, k;
1363
1364     if (first >= ATTRSTRING_1ST) {
1365         for (j = 0, k = first; j < MAX_ATTRSTRING; ++j, ++k) {
1366             if (k > ATTRSTRING_END)
1367                 break;
1368             attr_test_string[j] = (char) k;
1369             if (((k + 1 - first) % 5) == 0) {
1370                 if (++j >= MAX_ATTRSTRING)
1371                     break;
1372                 attr_test_string[j] = ' ';
1373             }
1374         }
1375         if ((LEN_ATTRSTRING - j) > 5) {
1376             attr_test_string[0] = save;
1377             adjust_attr_string(adjust - 1);
1378         } else {
1379             while (j < MAX_ATTRSTRING)
1380                 attr_test_string[j++] = ' ';
1381             attr_test_string[j] = '\0';
1382         }
1383     }
1384 }
1385
1386 /*
1387  * Prefer the right-end of the string for starting, since that maps to the
1388  * VT100 line-drawing.
1389  */
1390 static int
1391 default_attr_string(void)
1392 {
1393     int result = (ATTRSTRING_END - LEN_ATTRSTRING);
1394     result += (LEN_ATTRSTRING / 5);
1395     if (result < ATTRSTRING_1ST)
1396         result = ATTRSTRING_1ST;
1397     return result;
1398 }
1399
1400 static void
1401 init_attr_string(void)
1402 {
1403     attr_test_string[0] = (char) default_attr_string();
1404     adjust_attr_string(0);
1405 }
1406
1407 static int
1408 show_attr(WINDOW *win, int row, int skip, bool arrow, chtype attr, const char *name)
1409 {
1410     int ncv = get_ncv();
1411     chtype test = attr & (chtype) (~A_ALTCHARSET);
1412
1413     if (arrow)
1414         MvPrintw(row, 5, "-->");
1415     MvPrintw(row, 8, "%s mode:", name);
1416     MvPrintw(row, COL_ATTRSTRING - 1, "|");
1417     if (skip)
1418         printw("%*s", skip, " ");
1419     /*
1420      * Just for testing, write text using the alternate character set one
1421      * character at a time (to pass its rendition directly), and use the
1422      * string operation for the other attributes.
1423      */
1424     wmove(win, 0, 0);
1425     if (attr & A_ALTCHARSET) {
1426         const char *s;
1427         chtype ch;
1428
1429         for (s = attr_test_string; *s != '\0'; ++s) {
1430             ch = UChar(*s);
1431             (void) waddch(win, ch | attr);
1432         }
1433     } else {
1434         (void) wattrset(win, attr);
1435         (void) waddstr(win, attr_test_string);
1436         (void) wattroff(win, attr);
1437     }
1438     if (skip)
1439         printw("%*s", skip, " ");
1440     MvPrintw(row, COL_ATTRSTRING + LEN_ATTRSTRING, "|");
1441     if (test != A_NORMAL) {
1442         if (!(termattrs() & test)) {
1443             printw(" (N/A)");
1444         } else {
1445             if (ncv > 0 && stdscr && (getbkgd(stdscr) & A_COLOR)) {
1446                 static const chtype table[] =
1447                 {
1448                     A_STANDOUT,
1449                     A_UNDERLINE,
1450                     A_REVERSE,
1451                     A_BLINK,
1452                     A_DIM,
1453                     A_BOLD,
1454 #ifdef A_INVIS
1455                     A_INVIS,
1456 #endif
1457 #ifdef A_ITALIC
1458                     A_ITALIC,
1459 #endif
1460                     A_PROTECT,
1461                     A_ALTCHARSET
1462                 };
1463                 unsigned n;
1464                 bool found = FALSE;
1465                 for (n = 0; n < SIZEOF(table); n++) {
1466                     if ((table[n] & attr) != 0
1467                         && ((1 << n) & ncv) != 0) {
1468                         found = TRUE;
1469                         break;
1470                     }
1471                 }
1472                 if (found)
1473                     printw(" (NCV)");
1474             }
1475             if ((termattrs() & test) != test)
1476                 printw(" (Part)");
1477         }
1478     }
1479     return row + 2;
1480 }
1481
1482 typedef struct {
1483     attr_t attr;
1484     NCURSES_CONST char *name;
1485 } ATTR_TBL;
1486 /* *INDENT-OFF* */
1487 static const ATTR_TBL attrs_to_test[] = {
1488     { A_STANDOUT,       "STANDOUT" },
1489     { A_REVERSE,        "REVERSE" },
1490     { A_BOLD,           "BOLD" },
1491     { A_UNDERLINE,      "UNDERLINE" },
1492     { A_DIM,            "DIM" },
1493     { A_BLINK,          "BLINK" },
1494     { A_PROTECT,        "PROTECT" },
1495 #ifdef A_INVIS
1496     { A_INVIS,          "INVISIBLE" },
1497 #endif
1498 #ifdef A_ITALIC
1499     { A_ITALIC,         "ITALIC" },
1500 #endif
1501     { A_NORMAL,         "NORMAL" },
1502 };
1503 /* *INDENT-ON* */
1504
1505 static unsigned
1506 init_attr_list(ATTR_TBL * target, attr_t attrs)
1507 {
1508     unsigned result = 0;
1509     size_t n;
1510
1511     for (n = 0; n < SIZEOF(attrs_to_test); ++n) {
1512         attr_t test = attrs_to_test[n].attr;
1513         if (test == A_NORMAL || (test & attrs) != 0) {
1514             target[result++] = attrs_to_test[n];
1515         }
1516     }
1517     return result;
1518 }
1519
1520 static bool
1521 attr_getc(int *skip,
1522           NCURSES_COLOR_T *fg,
1523           NCURSES_COLOR_T *bg,
1524           NCURSES_COLOR_T *tx,
1525           int *ac,
1526           unsigned *kc,
1527           unsigned limit)
1528 {
1529     bool result = TRUE;
1530     bool error = FALSE;
1531     WINDOW *helpwin;
1532
1533     do {
1534         int ch = Getchar();
1535
1536         error = FALSE;
1537         if (ch < 256 && isdigit(ch)) {
1538             *skip = (ch - '0');
1539         } else {
1540             switch (ch) {
1541             case CTRL('L'):
1542                 Repaint();
1543                 break;
1544             case '?':
1545                 if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
1546                     box(helpwin, 0, 0);
1547                     attr_legend(helpwin);
1548                     wGetchar(helpwin);
1549                     delwin(helpwin);
1550                 }
1551                 break;
1552             case 'a':
1553                 *ac = 0;
1554                 break;
1555             case 'A':
1556                 *ac = A_ALTCHARSET;
1557                 break;
1558             case 'v':
1559                 if (*kc == 0)
1560                     *kc = limit - 1;
1561                 else
1562                     *kc -= 1;
1563                 break;
1564             case 'V':
1565                 *kc += 1;
1566                 if (*kc >= limit)
1567                     *kc = 0;
1568                 break;
1569             case '<':
1570                 adjust_attr_string(-1);
1571                 break;
1572             case '>':
1573                 adjust_attr_string(1);
1574                 break;
1575             case case_QUIT:
1576                 result = FALSE;
1577                 break;
1578             default:
1579                 error = cycle_color_attr(ch, fg, bg, tx);
1580                 break;
1581             }
1582         }
1583     } while (error);
1584     return result;
1585 }
1586
1587 static void
1588 attr_test(void)
1589 /* test text attributes */
1590 {
1591     int n;
1592     int skip = get_xmc();
1593     NCURSES_COLOR_T fg = COLOR_BLACK;   /* color pair 0 is special */
1594     NCURSES_COLOR_T bg = COLOR_BLACK;
1595     NCURSES_COLOR_T tx = -1;
1596     int ac = 0;
1597     unsigned j, k;
1598     WINDOW *my_wins[SIZEOF(attrs_to_test)];
1599     ATTR_TBL my_list[SIZEOF(attrs_to_test)];
1600     unsigned my_size = init_attr_list(my_list, termattrs());
1601
1602     if (my_size > 1) {
1603         for (j = 0; j < my_size; ++j) {
1604             my_wins[j] = subwin(stdscr,
1605                                 1, LEN_ATTRSTRING,
1606                                 2 + (int) (2 * j), COL_ATTRSTRING);
1607             scrollok(my_wins[j], FALSE);
1608         }
1609
1610         if (skip < 0)
1611             skip = 0;
1612
1613         n = skip;               /* make it easy */
1614         k = my_size - 1;
1615         init_attr_string();
1616
1617         do {
1618             int row = 2;
1619             chtype normal = A_NORMAL | BLANK;
1620             chtype extras = (chtype) ac;
1621
1622             if (use_colors) {
1623                 NCURSES_PAIRS_T pair = 0;
1624                 if ((fg != COLOR_BLACK) || (bg != COLOR_BLACK)) {
1625                     pair = 1;
1626                     if (init_pair(pair, fg, bg) == ERR) {
1627                         beep();
1628                     } else {
1629                         normal |= (chtype) COLOR_PAIR(pair);
1630                     }
1631                 }
1632                 if (tx >= 0) {
1633                     pair = 2;
1634                     if (init_pair(pair, tx, bg) == ERR) {
1635                         beep();
1636                     } else {
1637                         extras |= (chtype) COLOR_PAIR(pair);
1638                     }
1639                 }
1640             }
1641             bkgd(normal);
1642             bkgdset(normal);
1643             erase();
1644
1645             box(stdscr, 0, 0);
1646             MvAddStr(0, 20, "Character attribute test display");
1647
1648             for (j = 0; j < my_size; ++j) {
1649                 bool arrow = (j == k);
1650                 row = show_attr(my_wins[j], row, n, arrow,
1651                                 extras |
1652                                 my_list[j].attr |
1653                                 my_list[k].attr,
1654                                 my_list[j].name);
1655             }
1656
1657             MvPrintw(row, 8,
1658                      "This terminal does %shave the magic-cookie glitch",
1659                      get_xmc() > -1 ? "" : "not ");
1660             MvPrintw(row + 1, 8, "Enter '?' for help.");
1661             show_color_attr(fg, bg, tx);
1662             printw("  ACS (%d)", ac != 0);
1663
1664             refresh();
1665         } while (attr_getc(&n, &fg, &bg, &tx, &ac, &k, my_size));
1666
1667         bkgdset(A_NORMAL | BLANK);
1668         erase();
1669         endwin();
1670     } else {
1671         Cannot("does not support video attributes.");
1672     }
1673 }
1674
1675 #if USE_WIDEC_SUPPORT
1676 static wchar_t wide_attr_test_string[MAX_ATTRSTRING + 1];
1677
1678 static void
1679 wide_adjust_attr_string(int adjust)
1680 {
1681     char save = (char) wide_attr_test_string[0];
1682     int first = ((int) UChar(save)) + adjust;
1683     int j, k;
1684
1685     if (first >= ATTRSTRING_1ST) {
1686         for (j = 0, k = first; j < MAX_ATTRSTRING; ++j, ++k) {
1687             if (k > ATTRSTRING_END)
1688                 break;
1689             wide_attr_test_string[j] = k;
1690             if (((k + 1 - first) % 5) == 0) {
1691                 if (++j >= MAX_ATTRSTRING)
1692                     break;
1693                 wide_attr_test_string[j] = ' ';
1694             }
1695         }
1696         if ((LEN_ATTRSTRING - j) > 5) {
1697             wide_attr_test_string[0] = save;
1698             wide_adjust_attr_string(adjust - 1);
1699         } else {
1700             while (j < MAX_ATTRSTRING)
1701                 wide_attr_test_string[j++] = ' ';
1702             wide_attr_test_string[j] = '\0';
1703         }
1704     }
1705 }
1706
1707 static void
1708 wide_init_attr_string(void)
1709 {
1710     wide_attr_test_string[0] = default_attr_string();
1711     wide_adjust_attr_string(0);
1712 }
1713
1714 static void
1715 set_wide_background(NCURSES_PAIRS_T pair)
1716 {
1717     cchar_t normal;
1718     wchar_t blank[2];
1719
1720     blank[0] = ' ';
1721     blank[1] = 0;
1722     setcchar(&normal, blank, A_NORMAL, pair, 0);
1723     bkgrnd(&normal);
1724     bkgrndset(&normal);
1725 }
1726
1727 static attr_t
1728 get_wide_background(void)
1729 {
1730     attr_t result = A_NORMAL;
1731     attr_t attr;
1732     cchar_t ch;
1733     NCURSES_PAIRS_T pair;
1734     wchar_t wch[10];
1735
1736     memset(&ch, 0, sizeof(ch));
1737     if (getbkgrnd(&ch) != ERR) {
1738         if (getcchar(&ch, wch, &attr, &pair, 0) != ERR) {
1739             result = attr;
1740         }
1741     }
1742     return result;
1743 }
1744
1745 static int
1746 wide_show_attr(WINDOW *win,
1747                int row,
1748                int skip,
1749                bool arrow,
1750                chtype attr,
1751                NCURSES_PAIRS_T pair,
1752                const char *name)
1753 {
1754     int ncv = get_ncv();
1755     chtype test = attr & ~WA_ALTCHARSET;
1756
1757     if (arrow)
1758         MvPrintw(row, 5, "-->");
1759     MvPrintw(row, 8, "%s mode:", name);
1760     MvPrintw(row, COL_ATTRSTRING - 1, "|");
1761     if (skip)
1762         printw("%*s", skip, " ");
1763
1764     /*
1765      * Just for testing, write text using the alternate character set one
1766      * character at a time (to pass its rendition directly), and use the
1767      * string operation for the other attributes.
1768      */
1769     wmove(win, 0, 0);
1770     if (attr & WA_ALTCHARSET) {
1771         const wchar_t *s;
1772         cchar_t ch;
1773
1774         for (s = wide_attr_test_string; *s != L'\0'; ++s) {
1775             wchar_t fill[2];
1776             fill[0] = *s;
1777             fill[1] = L'\0';
1778             setcchar(&ch, fill, attr, pair, 0);
1779             (void) wadd_wch(win, &ch);
1780         }
1781     } else {
1782         attr_t old_attr = 0;
1783         NCURSES_PAIRS_T old_pair = 0;
1784
1785         (void) (wattr_get) (win, &old_attr, &old_pair, 0);
1786         (void) wattr_set(win, attr, pair, 0);
1787         (void) waddwstr(win, wide_attr_test_string);
1788         (void) wattr_set(win, old_attr, old_pair, 0);
1789     }
1790     if (skip)
1791         printw("%*s", skip, " ");
1792     MvPrintw(row, COL_ATTRSTRING + LEN_ATTRSTRING, "|");
1793     if (test != A_NORMAL) {
1794         if (!(term_attrs() & test)) {
1795             printw(" (N/A)");
1796         } else {
1797             if (ncv > 0 && (get_wide_background() & A_COLOR)) {
1798                 static const attr_t table[] =
1799                 {
1800                     WA_STANDOUT,
1801                     WA_UNDERLINE,
1802                     WA_REVERSE,
1803                     WA_BLINK,
1804                     WA_DIM,
1805                     WA_BOLD,
1806                     WA_INVIS,
1807                     WA_PROTECT,
1808                     WA_ALTCHARSET
1809                 };
1810                 unsigned n;
1811                 bool found = FALSE;
1812                 for (n = 0; n < SIZEOF(table); n++) {
1813                     if ((table[n] & attr) != 0
1814                         && ((1 << n) & ncv) != 0) {
1815                         found = TRUE;
1816                         break;
1817                     }
1818                 }
1819                 if (found)
1820                     printw(" (NCV)");
1821             }
1822             if ((term_attrs() & test) != test)
1823                 printw(" (Part)");
1824         }
1825     }
1826     return row + 2;
1827 }
1828
1829 static bool
1830 wide_attr_getc(int *skip,
1831                NCURSES_COLOR_T *fg, NCURSES_COLOR_T *bg,
1832                NCURSES_COLOR_T *tx, int *ac,
1833                unsigned *kc, unsigned limit)
1834 {
1835     bool result = TRUE;
1836     bool error = FALSE;
1837     WINDOW *helpwin;
1838
1839     do {
1840         int ch = Getchar();
1841
1842         error = FALSE;
1843         if (ch < 256 && isdigit(ch)) {
1844             *skip = (ch - '0');
1845         } else {
1846             switch (ch) {
1847             case CTRL('L'):
1848                 Repaint();
1849                 break;
1850             case '?':
1851                 if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
1852                     box_set(helpwin, 0, 0);
1853                     attr_legend(helpwin);
1854                     wGetchar(helpwin);
1855                     delwin(helpwin);
1856                 }
1857                 break;
1858             case 'a':
1859                 *ac = 0;
1860                 break;
1861             case 'A':
1862                 *ac = A_ALTCHARSET;
1863                 break;
1864             case 'v':
1865                 if (*kc == 0)
1866                     *kc = limit - 1;
1867                 else
1868                     *kc -= 1;
1869                 break;
1870             case 'V':
1871                 *kc += 1;
1872                 if (*kc >= limit)
1873                     *kc = 0;
1874                 break;
1875             case '<':
1876                 wide_adjust_attr_string(-1);
1877                 break;
1878             case '>':
1879                 wide_adjust_attr_string(1);
1880                 break;
1881             case case_QUIT:
1882                 result = FALSE;
1883                 break;
1884             default:
1885                 error = cycle_color_attr(ch, fg, bg, tx);
1886                 break;
1887             }
1888         }
1889     } while (error);
1890     return result;
1891 }
1892
1893 static void
1894 wide_attr_test(void)
1895 /* test text attributes using wide-character calls */
1896 {
1897     int n;
1898     int skip = get_xmc();
1899     NCURSES_COLOR_T fg = COLOR_BLACK;   /* color pair 0 is special */
1900     NCURSES_COLOR_T bg = COLOR_BLACK;
1901     NCURSES_COLOR_T tx = -1;
1902     int ac = 0;
1903     unsigned j, k;
1904     ATTR_TBL my_list[SIZEOF(attrs_to_test)];
1905     WINDOW *my_wins[SIZEOF(attrs_to_test)];
1906     unsigned my_size = init_attr_list(my_list, term_attrs());
1907
1908     if (my_size > 1) {
1909         for (j = 0; j < my_size; ++j) {
1910             my_wins[j] = subwin(stdscr,
1911                                 1, LEN_ATTRSTRING,
1912                                 2 + (int) (2 * j), COL_ATTRSTRING);
1913             scrollok(my_wins[j], FALSE);
1914         }
1915
1916         if (skip < 0)
1917             skip = 0;
1918
1919         n = skip;               /* make it easy */
1920         k = my_size - 1;
1921         wide_init_attr_string();
1922
1923         do {
1924             int row = 2;
1925             NCURSES_PAIRS_T pair = 0;
1926             NCURSES_PAIRS_T extras = 0;
1927
1928             if (use_colors) {
1929                 pair = (NCURSES_PAIRS_T) (fg != COLOR_BLACK || bg != COLOR_BLACK);
1930                 if (pair != 0) {
1931                     pair = 1;
1932                     if (init_pair(pair, fg, bg) == ERR) {
1933                         beep();
1934                     }
1935                 }
1936                 extras = pair;
1937                 if (tx >= 0) {
1938                     extras = 2;
1939                     if (init_pair(extras, tx, bg) == ERR) {
1940                         beep();
1941                     }
1942                 }
1943             }
1944             set_wide_background(pair);
1945             erase();
1946
1947             box_set(stdscr, 0, 0);
1948             MvAddStr(0, 20, "Character attribute test display");
1949
1950             for (j = 0; j < my_size; ++j) {
1951                 row = wide_show_attr(my_wins[j], row, n, (j == k),
1952                                      ((attr_t) ac |
1953                                       my_list[j].attr |
1954                                       my_list[k].attr),
1955                                      extras,
1956                                      my_list[j].name);
1957             }
1958
1959             MvPrintw(row, 8,
1960                      "This terminal does %shave the magic-cookie glitch",
1961                      get_xmc() > -1 ? "" : "not ");
1962             MvPrintw(row + 1, 8, "Enter '?' for help.");
1963             show_color_attr(fg, bg, tx);
1964             printw("  ACS (%d)", ac != 0);
1965
1966             refresh();
1967         } while (wide_attr_getc(&n, &fg, &bg, &tx, &ac, &k, my_size));
1968
1969         set_wide_background(0);
1970         erase();
1971         endwin();
1972     } else {
1973         Cannot("does not support extended video attributes.");
1974     }
1975 }
1976 #endif
1977
1978 /****************************************************************************
1979  *
1980  * Color support tests
1981  *
1982  ****************************************************************************/
1983
1984 static NCURSES_CONST char *the_color_names[] =
1985 {
1986     "black",
1987     "red",
1988     "green",
1989     "yellow",
1990     "blue",
1991     "magenta",
1992     "cyan",
1993     "white",
1994     "BLACK",
1995     "RED",
1996     "GREEN",
1997     "YELLOW",
1998     "BLUE",
1999     "MAGENTA",
2000     "CYAN",
2001     "WHITE"
2002 };
2003
2004 static void
2005 show_color_name(int y, int x, int color, bool wide)
2006 {
2007     if (move(y, x) != ERR) {
2008         char temp[80];
2009         int width = 8;
2010
2011         if (wide) {
2012             sprintf(temp, "%02d", color);
2013             width = 4;
2014         } else if (color >= 8) {
2015             sprintf(temp, "[%02d]", color);
2016         } else if (color < 0) {
2017             strcpy(temp, "default");
2018         } else {
2019             sprintf(temp, "%.*s", 16, the_color_names[color]);
2020         }
2021         printw("%-*.*s", width, width, temp);
2022     }
2023 }
2024
2025 static void
2026 color_legend(WINDOW *helpwin, bool wide)
2027 {
2028     int row = 1;
2029     int col = 1;
2030
2031     MvWPrintw(helpwin, row++, col,
2032               "ESC to exit.");
2033     ++row;
2034     MvWPrintw(helpwin, row++, col,
2035               "Use up/down arrow to scroll through the display if it is");
2036     MvWPrintw(helpwin, row++, col,
2037               "longer than one screen. Control/N and Control/P can be used");
2038     MvWPrintw(helpwin, row++, col,
2039               "in place of up/down arrow.  Use pageup/pagedown to scroll a");
2040     MvWPrintw(helpwin, row++, col,
2041               "full screen; control/B and control/F can be used here.");
2042     ++row;
2043     MvWPrintw(helpwin, row++, col,
2044               "Toggles:");
2045     MvWPrintw(helpwin, row++, col,
2046               "  a/A     toggle altcharset off/on");
2047     MvWPrintw(helpwin, row++, col,
2048               "  b/B     toggle bold off/on");
2049     if (has_colors()) {
2050         MvWPrintw(helpwin, row++, col,
2051                   "  c/C     cycle used-colors through 8,16,...,COLORS");
2052     }
2053     MvWPrintw(helpwin, row++, col,
2054               "  n/N     toggle text/number on/off");
2055     MvWPrintw(helpwin, row++, col,
2056               "  r/R     toggle reverse on/off");
2057     MvWPrintw(helpwin, row++, col,
2058               "  w/W     toggle width between 8/16 colors");
2059 #if USE_WIDEC_SUPPORT
2060     if (wide) {
2061         MvWPrintw(helpwin, row++, col,
2062                   "Wide characters:");
2063         MvWPrintw(helpwin, row, col,
2064                   "  x/X     toggle text between ASCII and wide-character");
2065     }
2066 #else
2067     (void) wide;
2068 #endif
2069 }
2070
2071 #define set_color_test(name, value) if (name != value) { name = value; base_row = 0; }
2072
2073 static int
2074 color_cycle(int current, int step)
2075 {
2076     int result = current;
2077     if (step < 0) {
2078         if (current <= 8) {
2079             result = COLORS;
2080         } else {
2081             result = 8;
2082             if ((result * 2) > COLORS) {
2083                 result = COLORS;
2084             } else {
2085                 while ((result * 2) < current) {
2086                     result *= 2;
2087                 }
2088             }
2089         }
2090     } else {
2091         if (current >= COLORS) {
2092             result = 8;
2093         } else {
2094             result *= 2;
2095         }
2096         if (result > COLORS)
2097             result = COLORS;
2098     }
2099     return result;
2100 }
2101
2102 /* generate a color test pattern */
2103 static void
2104 color_test(void)
2105 {
2106     NCURSES_PAIRS_T i;
2107     int top = 0, width;
2108     int base_row = 0;
2109     int grid_top = top + 3;
2110     int page_size = (LINES - grid_top);
2111     int pairs_max;
2112     int colors_max = COLORS;
2113     int row_limit;
2114     int per_row;
2115     char numbered[80];
2116     const char *hello;
2117     bool done = FALSE;
2118     bool opt_acsc = FALSE;
2119     bool opt_bold = FALSE;
2120     bool opt_revs = FALSE;
2121     bool opt_nums = FALSE;
2122     bool opt_wide = FALSE;
2123     WINDOW *helpwin;
2124
2125     while (!done) {
2126         int shown = 0;
2127
2128         pairs_max = PAIR_NUMBER(A_COLOR) + 1;
2129         if (colors_max * colors_max <= COLOR_PAIRS) {
2130             int limit = (colors_max - min_colors) * (colors_max - min_colors);
2131             if (pairs_max > limit)
2132                 pairs_max = limit;
2133         } else {
2134             if (pairs_max > COLOR_PAIRS)
2135                 pairs_max = COLOR_PAIRS;
2136         }
2137
2138         /* this assumes an 80-column line */
2139         if (opt_wide) {
2140             width = 4;
2141             hello = "Test";
2142             per_row = (colors_max > 8) ? 16 : 8;
2143         } else {
2144             width = 8;
2145             hello = "Hello";
2146             per_row = 8;
2147         }
2148         per_row -= min_colors;
2149
2150         row_limit = (pairs_max + per_row - 1) / per_row;
2151
2152         move(0, 0);
2153         (void) printw("There are %d color pairs and %d colors",
2154                       pairs_max, COLORS);
2155         if (colors_max != COLORS)
2156             (void) printw(" (using %d colors)", colors_max);
2157         if (min_colors)
2158             (void) addstr(" besides 'default'");
2159
2160         clrtobot();
2161         MvPrintw(top + 1, 0,
2162                  "%dx%d matrix of foreground/background colors, bold *%s*\n",
2163                  row_limit,
2164                  per_row,
2165                  opt_bold ? "on" : "off");
2166
2167         /* show color names/numbers across the top */
2168         for (i = 0; i < per_row; i++)
2169             show_color_name(top + 2, (i + 1) * width, i + min_colors, opt_wide);
2170
2171         /* show a grid of colors, with color names/ numbers on the left */
2172         for (i = (NCURSES_PAIRS_T) (base_row * per_row); i < pairs_max; i++) {
2173             int row = grid_top + (i / per_row) - base_row;
2174             int col = (i % per_row + 1) * width;
2175             NCURSES_PAIRS_T pair = i;
2176
2177 #define InxToFG(i) (NCURSES_COLOR_T) ((i % (colors_max - min_colors)) + min_colors)
2178 #define InxToBG(i) (NCURSES_COLOR_T) ((i / (colors_max - min_colors)) + min_colors)
2179             if (row >= 0 && move(row, col) != ERR) {
2180                 NCURSES_COLOR_T fg = InxToFG(i);
2181                 NCURSES_COLOR_T bg = InxToBG(i);
2182
2183                 init_pair(pair, fg, bg);
2184                 attron((attr_t) COLOR_PAIR(pair));
2185                 if (opt_acsc)
2186                     attron((attr_t) A_ALTCHARSET);
2187                 if (opt_bold)
2188                     attron((attr_t) A_BOLD);
2189                 if (opt_revs)
2190                     attron((attr_t) A_REVERSE);
2191
2192                 if (opt_nums) {
2193                     sprintf(numbered, "{%02X}", (int) i);
2194                     hello = numbered;
2195                 }
2196                 printw("%-*.*s", width, width, hello);
2197                 (void) attrset(A_NORMAL);
2198
2199                 if ((i % per_row) == 0 && InxToFG(i) == min_colors) {
2200                     show_color_name(row, 0, InxToBG(i), opt_wide);
2201                 }
2202                 ++shown;
2203             } else if (shown) {
2204                 break;
2205             }
2206         }
2207
2208         switch (wGetchar(stdscr)) {
2209         case 'a':
2210             opt_acsc = FALSE;
2211             break;
2212         case 'A':
2213             opt_acsc = TRUE;
2214             break;
2215         case 'b':
2216             opt_bold = FALSE;
2217             break;
2218         case 'B':
2219             opt_bold = TRUE;
2220             break;
2221         case 'c':
2222             colors_max = color_cycle(colors_max, -1);
2223             break;
2224         case 'C':
2225             colors_max = color_cycle(colors_max, 1);
2226             break;
2227         case 'n':
2228             opt_nums = FALSE;
2229             break;
2230         case 'N':
2231             opt_nums = TRUE;
2232             break;
2233         case 'r':
2234             opt_revs = FALSE;
2235             break;
2236         case 'R':
2237             opt_revs = TRUE;
2238             break;
2239         case case_QUIT:
2240             done = TRUE;
2241             continue;
2242         case 'w':
2243             set_color_test(opt_wide, FALSE);
2244             break;
2245         case 'W':
2246             set_color_test(opt_wide, TRUE);
2247             break;
2248         case CTRL('p'):
2249         case KEY_UP:
2250             if (base_row <= 0) {
2251                 beep();
2252             } else {
2253                 base_row -= 1;
2254             }
2255             break;
2256         case CTRL('n'):
2257         case KEY_DOWN:
2258             if (base_row + page_size >= row_limit) {
2259                 beep();
2260             } else {
2261                 base_row += 1;
2262             }
2263             break;
2264         case CTRL('b'):
2265         case KEY_PREVIOUS:
2266         case KEY_PPAGE:
2267             if (base_row <= 0) {
2268                 beep();
2269             } else {
2270                 base_row -= (page_size - 1);
2271                 if (base_row < 0)
2272                     base_row = 0;
2273             }
2274             break;
2275         case CTRL('f'):
2276         case KEY_NEXT:
2277         case KEY_NPAGE:
2278             if (base_row + page_size >= row_limit) {
2279                 beep();
2280             } else {
2281                 base_row += page_size - 1;
2282                 if (base_row + page_size >= row_limit) {
2283                     base_row = row_limit - page_size - 1;
2284                 }
2285             }
2286             break;
2287         case '?':
2288             if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
2289                 box(helpwin, 0, 0);
2290                 color_legend(helpwin, FALSE);
2291                 wGetchar(helpwin);
2292                 delwin(helpwin);
2293             }
2294             break;
2295         default:
2296             beep();
2297             continue;
2298         }
2299     }
2300
2301     erase();
2302     endwin();
2303 }
2304
2305 #if USE_WIDEC_SUPPORT
2306 /* generate a color test pattern */
2307 static void
2308 wide_color_test(void)
2309 {
2310     int i;
2311     int top = 0, width;
2312     int base_row = 0;
2313     int grid_top = top + 3;
2314     int page_size = (LINES - grid_top);
2315     int pairs_max = (unsigned short) (-1);
2316     int colors_max = COLORS;
2317     int row_limit;
2318     int per_row;
2319     char numbered[80];
2320     const char *hello;
2321     bool done = FALSE;
2322     bool opt_acsc = FALSE;
2323     bool opt_bold = FALSE;
2324     bool opt_revs = FALSE;
2325     bool opt_wide = FALSE;
2326     bool opt_nums = FALSE;
2327     bool opt_xchr = FALSE;
2328     wchar_t buffer[80];
2329     WINDOW *helpwin;
2330
2331     while (!done) {
2332         int shown = 0;
2333
2334         pairs_max = (unsigned short) (-1);
2335         if (colors_max * colors_max <= COLOR_PAIRS) {
2336             int limit = (colors_max - min_colors) * (colors_max - min_colors);
2337             if (pairs_max > limit)
2338                 pairs_max = limit;
2339         } else {
2340             if (pairs_max > COLOR_PAIRS)
2341                 pairs_max = COLOR_PAIRS;
2342         }
2343
2344         /* this assumes an 80-column line */
2345         if (opt_wide) {
2346             width = 4;
2347             hello = "Test";
2348             per_row = (colors_max > 8) ? 16 : 8;
2349         } else {
2350             width = 8;
2351             hello = "Hello";
2352             per_row = 8;
2353         }
2354         per_row -= min_colors;
2355
2356         if (opt_xchr) {
2357             make_fullwidth_text(buffer, hello);
2358             width *= 2;
2359             per_row /= 2;
2360         } else {
2361             make_narrow_text(buffer, hello);
2362         }
2363
2364         row_limit = (pairs_max + per_row - 1) / per_row;
2365
2366         move(0, 0);
2367         (void) printw("There are %d color pairs and %d colors",
2368                       pairs_max, COLORS);
2369         if (colors_max != COLORS)
2370             (void) printw(" (using %d colors)", colors_max);
2371         if (min_colors)
2372             (void) addstr(" besides 'default'");
2373
2374         clrtobot();
2375         MvPrintw(top + 1, 0,
2376                  "%dx%d matrix of foreground/background colors, bold *%s*\n",
2377                  row_limit,
2378                  per_row,
2379                  opt_bold ? "on" : "off");
2380
2381         /* show color names/numbers across the top */
2382         for (i = 0; i < per_row; i++)
2383             show_color_name(top + 2, (i + 1) * width, i + min_colors, opt_wide);
2384
2385         /* show a grid of colors, with color names/ numbers on the left */
2386         for (i = (base_row * per_row); i < pairs_max; i++) {
2387             int row = grid_top + (i / per_row) - base_row;
2388             int col = (i % per_row + 1) * width;
2389             NCURSES_PAIRS_T pair = (NCURSES_PAIRS_T) i;
2390
2391             if (row >= 0 && move(row, col) != ERR) {
2392                 init_pair(pair, InxToFG(i), InxToBG(i));
2393                 (void) color_set(pair, NULL);
2394                 if (opt_acsc)
2395                     attr_on((attr_t) A_ALTCHARSET, NULL);
2396                 if (opt_bold)
2397                     attr_on((attr_t) A_BOLD, NULL);
2398                 if (opt_revs)
2399                     attr_on((attr_t) A_REVERSE, NULL);
2400
2401                 if (opt_nums) {
2402                     sprintf(numbered, "{%02X}", i);
2403                     if (opt_xchr) {
2404                         make_fullwidth_text(buffer, numbered);
2405                     } else {
2406                         make_narrow_text(buffer, numbered);
2407                     }
2408                 }
2409                 addnwstr(buffer, width);
2410                 (void) attr_set(A_NORMAL, 0, NULL);
2411
2412                 if ((i % per_row) == 0 && InxToFG(i) == min_colors) {
2413                     show_color_name(row, 0, InxToBG(i), opt_wide);
2414                 }
2415                 ++shown;
2416             } else if (shown) {
2417                 break;
2418             }
2419         }
2420
2421         switch (wGetchar(stdscr)) {
2422         case 'a':
2423             opt_acsc = FALSE;
2424             break;
2425         case 'A':
2426             opt_acsc = TRUE;
2427             break;
2428         case 'b':
2429             opt_bold = FALSE;
2430             break;
2431         case 'B':
2432             opt_bold = TRUE;
2433             break;
2434         case 'c':
2435             colors_max = color_cycle(colors_max, -1);
2436             break;
2437         case 'C':
2438             colors_max = color_cycle(colors_max, 1);
2439             break;
2440         case 'n':
2441             opt_nums = FALSE;
2442             break;
2443         case 'N':
2444             opt_nums = TRUE;
2445             break;
2446         case 'r':
2447             opt_revs = FALSE;
2448             break;
2449         case 'R':
2450             opt_revs = TRUE;
2451             break;
2452         case case_QUIT:
2453             done = TRUE;
2454             continue;
2455         case 'w':
2456             set_color_test(opt_wide, FALSE);
2457             break;
2458         case 'W':
2459             set_color_test(opt_wide, TRUE);
2460             break;
2461         case 'x':
2462             opt_xchr = FALSE;
2463             break;
2464         case 'X':
2465             opt_xchr = TRUE;
2466             break;
2467         case CTRL('p'):
2468         case KEY_UP:
2469             if (base_row <= 0) {
2470                 beep();
2471             } else {
2472                 base_row -= 1;
2473             }
2474             break;
2475         case CTRL('n'):
2476         case KEY_DOWN:
2477             if (base_row + page_size >= row_limit) {
2478                 beep();
2479             } else {
2480                 base_row += 1;
2481             }
2482             break;
2483         case CTRL('b'):
2484         case KEY_PREVIOUS:
2485         case KEY_PPAGE:
2486             if (base_row <= 0) {
2487                 beep();
2488             } else {
2489                 base_row -= (page_size - 1);
2490                 if (base_row < 0)
2491                     base_row = 0;
2492             }
2493             break;
2494         case CTRL('f'):
2495         case KEY_NEXT:
2496         case KEY_NPAGE:
2497             if (base_row + page_size >= row_limit) {
2498                 beep();
2499             } else {
2500                 base_row += page_size - 1;
2501                 if (base_row + page_size >= row_limit) {
2502                     base_row = row_limit - page_size - 1;
2503                 }
2504             }
2505             break;
2506         case '?':
2507             if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
2508                 box(helpwin, 0, 0);
2509                 color_legend(helpwin, TRUE);
2510                 wGetchar(helpwin);
2511                 delwin(helpwin);
2512             }
2513             break;
2514         default:
2515             beep();
2516             continue;
2517         }
2518     }
2519
2520     erase();
2521     endwin();
2522 }
2523 #endif /* USE_WIDEC_SUPPORT */
2524
2525 static void
2526 change_color(NCURSES_PAIRS_T current, int field, int value, int usebase)
2527 {
2528     NCURSES_COLOR_T red, green, blue;
2529
2530     color_content(current, &red, &green, &blue);
2531
2532     switch (field) {
2533     case 0:
2534         red = (NCURSES_COLOR_T) (usebase ? (red + value) : value);
2535         break;
2536     case 1:
2537         green = (NCURSES_COLOR_T) (usebase ? (green + value) : value);
2538         break;
2539     case 2:
2540         blue = (NCURSES_COLOR_T) (usebase ? (blue + value) : value);
2541         break;
2542     }
2543
2544     if (init_color(current, red, green, blue) == ERR)
2545         beep();
2546 }
2547
2548 static void
2549 init_all_colors(void)
2550 {
2551     NCURSES_PAIRS_T c;
2552
2553     for (c = 0; c < COLORS; ++c)
2554         init_color(c,
2555                    all_colors[c].red,
2556                    all_colors[c].green,
2557                    all_colors[c].blue);
2558 }
2559
2560 #define scaled_rgb(n) ((255 * (n)) / 1000)
2561
2562 static void
2563 color_edit(void)
2564 /* display the color test pattern, without trying to edit colors */
2565 {
2566     int i;
2567     int current = 0;
2568     int this_c = 0, value = 0, field = 0;
2569     int last_c;
2570     int top_color = 0;
2571     int page_size = (LINES - 6);
2572
2573     init_all_colors();
2574     refresh();
2575
2576     for (i = 0; i < max_colors; i++)
2577         init_pair((NCURSES_PAIRS_T) i,
2578                   (NCURSES_COLOR_T) COLOR_WHITE,
2579                   (NCURSES_COLOR_T) i);
2580
2581     MvPrintw(LINES - 2, 0, "Number: %d", value);
2582
2583     do {
2584         NCURSES_COLOR_T red, green, blue;
2585
2586         attron(A_BOLD);
2587         MvAddStr(0, 20, "Color RGB Value Editing");
2588         attroff(A_BOLD);
2589
2590         for (i = (NCURSES_COLOR_T) top_color;
2591              (i - top_color < page_size)
2592              && (i < max_colors); i++) {
2593             char numeric[80];
2594
2595             sprintf(numeric, "[%d]", i);
2596             MvPrintw(2 + i - top_color, 0, "%c %-8s:",
2597                      (i == current ? '>' : ' '),
2598                      (i < (int) SIZEOF(the_color_names)
2599                       ? the_color_names[i] : numeric));
2600             (void) attrset((attr_t) COLOR_PAIR(i));
2601             addstr("        ");
2602             (void) attrset(A_NORMAL);
2603
2604             color_content((NCURSES_PAIRS_T) i, &red, &green, &blue);
2605             addstr("   R = ");
2606             if (current == i && field == 0)
2607                 attron(A_STANDOUT);
2608             printw("%04d", (int) red);
2609             if (current == i && field == 0)
2610                 (void) attrset(A_NORMAL);
2611             addstr(", G = ");
2612             if (current == i && field == 1)
2613                 attron(A_STANDOUT);
2614             printw("%04d", (int) green);
2615             if (current == i && field == 1)
2616                 (void) attrset(A_NORMAL);
2617             addstr(", B = ");
2618             if (current == i && field == 2)
2619                 attron(A_STANDOUT);
2620             printw("%04d", (int) blue);
2621             if (current == i && field == 2)
2622                 (void) attrset(A_NORMAL);
2623             (void) attrset(A_NORMAL);
2624             printw(" ( %3d %3d %3d )",
2625                    (int) scaled_rgb(red),
2626                    (int) scaled_rgb(green),
2627                    (int) scaled_rgb(blue));
2628         }
2629
2630         MvAddStr(LINES - 3, 0,
2631                  "Use up/down to select a color, left/right to change fields.");
2632         MvAddStr(LINES - 2, 0,
2633                  "Modify field by typing nnn=, nnn-, or nnn+.  ? for help.");
2634
2635         move(2 + current - top_color, 0);
2636
2637         last_c = this_c;
2638         this_c = Getchar();
2639         if (this_c < 256 && isdigit(this_c) && !isdigit(last_c))
2640             value = 0;
2641
2642         switch (this_c) {
2643         case CTRL('b'):
2644         case KEY_PPAGE:
2645             if (current > 0)
2646                 current -= (page_size - 1);
2647             else
2648                 beep();
2649             break;
2650
2651         case CTRL('f'):
2652         case KEY_NPAGE:
2653             if (current < (max_colors - 1))
2654                 current += (page_size - 1);
2655             else
2656                 beep();
2657             break;
2658
2659         case CTRL('p'):
2660         case KEY_UP:
2661             current = (current == 0 ? (max_colors - 1) : current - 1);
2662             break;
2663
2664         case CTRL('n'):
2665         case KEY_DOWN:
2666             current = (current == (max_colors - 1) ? 0 : current + 1);
2667             break;
2668
2669         case KEY_RIGHT:
2670             field = (field == 2 ? 0 : field + 1);
2671             break;
2672
2673         case KEY_LEFT:
2674             field = (field == 0 ? 2 : field - 1);
2675             break;
2676
2677         case '0':
2678         case '1':
2679         case '2':
2680         case '3':
2681         case '4':
2682         case '5':
2683         case '6':
2684         case '7':
2685         case '8':
2686         case '9':
2687             value = value * 10 + (this_c - '0');
2688             break;
2689
2690         case '+':
2691             change_color((NCURSES_PAIRS_T) current, field, value, 1);
2692             break;
2693
2694         case '-':
2695             change_color((NCURSES_PAIRS_T) current, field, -value, 1);
2696             break;
2697
2698         case '=':
2699             change_color((NCURSES_PAIRS_T) current, field, value, 0);
2700             break;
2701
2702         case '?':
2703             erase();
2704             P("                      RGB Value Editing Help");
2705             P("");
2706             P("You are in the RGB value editor.  Use the arrow keys to select one of");
2707             P("the fields in one of the RGB triples of the current colors; the one");
2708             P("currently selected will be reverse-video highlighted.");
2709             P("");
2710             P("To change a field, enter the digits of the new value; they are echoed");
2711             P("as entered.  Finish by typing `='.  The change will take effect instantly.");
2712             P("To increment or decrement a value, use the same procedure, but finish");
2713             P("with a `+' or `-'.");
2714             P("");
2715             P("Press 'm' to invoke the top-level menu with the current color settings.");
2716             P("To quit, do ESC");
2717
2718             Pause();
2719             erase();
2720             break;
2721
2722         case 'm':
2723             endwin();
2724             main_menu(FALSE);
2725             for (i = 0; i < max_colors; i++)
2726                 init_pair((NCURSES_PAIRS_T) i,
2727                           (NCURSES_COLOR_T) COLOR_WHITE,
2728                           (NCURSES_COLOR_T) i);
2729             refresh();
2730             break;
2731
2732         case case_QUIT:
2733             break;
2734
2735         default:
2736             beep();
2737             break;
2738         }
2739
2740         if (current < 0)
2741             current = 0;
2742         if (current >= max_colors)
2743             current = max_colors - 1;
2744         if (current < top_color)
2745             top_color = current;
2746         if (current - top_color >= page_size)
2747             top_color = current - (page_size - 1);
2748
2749         MvPrintw(LINES - 1, 0, "Number: %d", value);
2750         clrtoeol();
2751     } while
2752         (!isQuit(this_c));
2753
2754     erase();
2755
2756     /*
2757      * ncurses does not reset each color individually when calling endwin().
2758      */
2759     init_all_colors();
2760
2761     endwin();
2762 }
2763
2764 /****************************************************************************
2765  *
2766  * Alternate character-set stuff
2767  *
2768  ****************************************************************************/
2769 static bool
2770 cycle_attr(int ch, unsigned *at_code, chtype *attr, ATTR_TBL * list, unsigned limit)
2771 {
2772     bool result = TRUE;
2773
2774     switch (ch) {
2775     case 'v':
2776         if ((*at_code += 1) >= limit)
2777             *at_code = 0;
2778         break;
2779     case 'V':
2780         if (*at_code == 0)
2781             *at_code = limit - 1;
2782         else
2783             *at_code -= 1;
2784         break;
2785     default:
2786         result = FALSE;
2787         break;
2788     }
2789     if (result)
2790         *attr = list[*at_code].attr;
2791     return result;
2792 }
2793
2794 static bool
2795 cycle_colors(int ch, int *fg, int *bg, NCURSES_PAIRS_T *pair)
2796 {
2797     bool result = FALSE;
2798
2799     if (use_colors) {
2800         result = TRUE;
2801         switch (ch) {
2802         case 'F':
2803             if ((*fg -= 1) < 0)
2804                 *fg = COLORS - 1;
2805             break;
2806         case 'f':
2807             if ((*fg += 1) >= COLORS)
2808                 *fg = 0;
2809             break;
2810         case 'B':
2811             if ((*bg -= 1) < 0)
2812                 *bg = COLORS - 1;
2813             break;
2814         case 'b':
2815             if ((*bg += 1) >= COLORS)
2816                 *bg = 0;
2817             break;
2818         default:
2819             result = FALSE;
2820             break;
2821         }
2822         if (result) {
2823             *pair = (NCURSES_PAIRS_T) (*fg != COLOR_BLACK || *bg != COLOR_BLACK);
2824             if (*pair != 0) {
2825                 *pair = 1;
2826                 if (init_pair(*pair,
2827                               (NCURSES_COLOR_T) *fg,
2828                               (NCURSES_COLOR_T) *bg) == ERR) {
2829                     result = FALSE;
2830                 }
2831             }
2832         }
2833     }
2834     return result;
2835 }
2836
2837 /****************************************************************************
2838  *
2839  * Soft-key label test
2840  *
2841  ****************************************************************************/
2842
2843 #if USE_SOFTKEYS
2844
2845 #define SLK_HELP 17
2846 #define SLK_WORK (SLK_HELP + 3)
2847
2848 static void
2849 slk_help(void)
2850 {
2851     static const char *table[] =
2852     {
2853         "Available commands are:"
2854         ,""
2855         ,"^L         -- repaint this message and activate soft keys"
2856         ,"a/d        -- activate/disable soft keys"
2857         ,"c          -- set centered format for labels"
2858         ,"l          -- set left-justified format for labels"
2859         ,"r          -- set right-justified format for labels"
2860         ,"[12345678] -- set label; labels are numbered 1 through 8"
2861         ,"e          -- erase stdscr (should not erase labels)"
2862         ,"s          -- test scrolling of shortened screen"
2863         ,"v/V        -- cycle through video attributes"
2864 #if HAVE_SLK_COLOR
2865         ,"F/f/B/b    -- cycle through foreground/background colors"
2866 #endif
2867         ,"ESC        -- return to main menu"
2868         ,""
2869         ,"Note: if activating the soft keys causes your terminal to scroll up"
2870         ,"one line, your terminal auto-scrolls when anything is written to the"
2871         ,"last screen position.  The ncurses code does not yet handle this"
2872         ,"gracefully."
2873     };
2874     unsigned j;
2875
2876     move(2, 0);
2877     for (j = 0; j < SIZEOF(table); ++j) {
2878         P(table[j]);
2879     }
2880     refresh();
2881 }
2882
2883 #if HAVE_SLK_COLOR
2884 static void
2885 call_slk_color(int fg, int bg)
2886 {
2887     init_pair(1, (NCURSES_COLOR_T) bg, (NCURSES_COLOR_T) fg);
2888     slk_color(1);
2889     MvPrintw(SLK_WORK, 0, "Colors %d/%d\n", fg, bg);
2890     clrtoeol();
2891     slk_touch();
2892     slk_noutrefresh();
2893     refresh();
2894 }
2895 #endif
2896
2897 static void
2898 slk_test(void)
2899 /* exercise the soft keys */
2900 {
2901     int c, fmt = 1;
2902     char buf[9];
2903     char *s;
2904     chtype attr = A_NORMAL;
2905     unsigned at_code = 0;
2906 #if HAVE_SLK_COLOR
2907     int fg = COLOR_BLACK;
2908     int bg = COLOR_WHITE;
2909     NCURSES_PAIRS_T pair = 0;
2910 #endif
2911     ATTR_TBL my_list[SIZEOF(attrs_to_test)];
2912     unsigned my_size = init_attr_list(my_list, termattrs());
2913
2914     c = CTRL('l');
2915 #if HAVE_SLK_COLOR
2916     if (use_colors) {
2917         call_slk_color(fg, bg);
2918     }
2919 #endif
2920
2921     do {
2922         move(0, 0);
2923         switch (c) {
2924         case CTRL('l'):
2925             erase();
2926             attron(A_BOLD);
2927             MvAddStr(0, 20, "Soft Key Exerciser");
2928             attroff(A_BOLD);
2929
2930             slk_help();
2931             /* fall through */
2932
2933         case 'a':
2934             slk_restore();
2935             break;
2936
2937         case 'e':
2938             wclear(stdscr);
2939             break;
2940
2941         case 's':
2942             MvPrintw(SLK_WORK, 0, "Press Q to stop the scrolling-test: ");
2943             while ((c = Getchar()) != 'Q' && (c != ERR))
2944                 addch((chtype) c);
2945             break;
2946
2947         case 'd':
2948             slk_clear();
2949             break;
2950
2951         case 'l':
2952             fmt = 0;
2953             break;
2954
2955         case 'c':
2956             fmt = 1;
2957             break;
2958
2959         case 'r':
2960             fmt = 2;
2961             break;
2962
2963         case '1':
2964         case '2':
2965         case '3':
2966         case '4':
2967         case '5':
2968         case '6':
2969         case '7':
2970         case '8':
2971             MvAddStr(SLK_WORK, 0, "Please enter the label value: ");
2972             strcpy(buf, "");
2973             if ((s = slk_label(c - '0')) != 0) {
2974                 strncpy(buf, s, (size_t) 8);
2975             }
2976             wGetstring(stdscr, buf, 8);
2977             slk_set((c - '0'), buf, fmt);
2978             slk_refresh();
2979             move(SLK_WORK, 0);
2980             clrtobot();
2981             break;
2982
2983         case case_QUIT:
2984             goto done;
2985
2986 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
2987         case KEY_RESIZE:
2988             wnoutrefresh(stdscr);
2989             break;
2990 #endif
2991
2992         default:
2993             if (cycle_attr(c, &at_code, &attr, my_list, my_size)) {
2994                 slk_attrset(attr);
2995                 slk_touch();
2996                 slk_noutrefresh();
2997                 break;
2998             }
2999 #if HAVE_SLK_COLOR
3000             if (cycle_colors(c, &fg, &bg, &pair)) {
3001                 if (use_colors) {
3002                     call_slk_color(fg, bg);
3003                 } else {
3004                     beep();
3005                 }
3006                 break;
3007             }
3008 #endif
3009             beep();
3010             break;
3011         }
3012     } while (!isQuit(c = Getchar()));
3013
3014   done:
3015     slk_clear();
3016     erase();
3017     endwin();
3018 }
3019
3020 #if USE_WIDEC_SUPPORT
3021 #define SLKLEN 8
3022 static void
3023 wide_slk_test(void)
3024 /* exercise the soft keys */
3025 {
3026     int c, fmt = 1;
3027     wchar_t buf[SLKLEN + 1];
3028     char *s;
3029     chtype attr = A_NORMAL;
3030     unsigned at_code = 0;
3031     int fg = COLOR_BLACK;
3032     int bg = COLOR_WHITE;
3033     NCURSES_PAIRS_T pair = 0;
3034     ATTR_TBL my_list[SIZEOF(attrs_to_test)];
3035     unsigned my_size = init_attr_list(my_list, term_attrs());
3036
3037     c = CTRL('l');
3038     if (use_colors) {
3039         call_slk_color(fg, bg);
3040     }
3041     do {
3042         move(0, 0);
3043         switch (c) {
3044         case CTRL('l'):
3045             erase();
3046             attr_on(WA_BOLD, NULL);
3047             MvAddStr(0, 20, "Soft Key Exerciser");
3048             attr_off(WA_BOLD, NULL);
3049
3050             slk_help();
3051             /* fall through */
3052
3053         case 'a':
3054             slk_restore();
3055             break;
3056
3057         case 'e':
3058             wclear(stdscr);
3059             break;
3060
3061         case 's':
3062             MvPrintw(SLK_WORK, 0, "Press Q to stop the scrolling-test: ");
3063             while ((c = Getchar()) != 'Q' && (c != ERR))
3064                 addch((chtype) c);
3065             break;
3066
3067         case 'd':
3068             slk_clear();
3069             break;
3070
3071         case 'l':
3072             fmt = 0;
3073             break;
3074
3075         case 'c':
3076             fmt = 1;
3077             break;
3078
3079         case 'r':
3080             fmt = 2;
3081             break;
3082
3083         case '1':
3084         case '2':
3085         case '3':
3086         case '4':
3087         case '5':
3088         case '6':
3089         case '7':
3090         case '8':
3091             MvAddStr(SLK_WORK, 0, "Please enter the label value: ");
3092             *buf = 0;
3093             if ((s = slk_label(c - '0')) != 0) {
3094                 char *temp = strdup(s);
3095                 size_t used = strlen(temp);
3096                 size_t want = SLKLEN;
3097                 size_t test;
3098 #ifndef state_unused
3099                 mbstate_t state;
3100 #endif
3101
3102                 buf[0] = L'\0';
3103                 while (want > 0 && used != 0) {
3104                     const char *base = s;
3105                     reset_mbytes(state);
3106                     test = count_mbytes(base, 0, &state);
3107                     if (test == (size_t) -1) {
3108                         temp[--used] = 0;
3109                     } else if (test > want) {
3110                         temp[--used] = 0;
3111                     } else {
3112                         reset_mbytes(state);
3113                         trans_mbytes(buf, base, want, &state);
3114                         break;
3115                     }
3116                 }
3117                 free(temp);
3118             }
3119             wGet_wstring(stdscr, buf, SLKLEN);
3120             slk_wset((c - '0'), buf, fmt);
3121             slk_refresh();
3122             move(SLK_WORK, 0);
3123             clrtobot();
3124             break;
3125
3126         case case_QUIT:
3127             goto done;
3128
3129         case 'F':
3130             if (use_colors) {
3131                 fg = (NCURSES_COLOR_T) ((fg + 1) % COLORS);
3132                 call_slk_color(fg, bg);
3133             }
3134             break;
3135         case 'B':
3136             if (use_colors) {
3137                 bg = (NCURSES_COLOR_T) ((bg + 1) % COLORS);
3138                 call_slk_color(fg, bg);
3139             }
3140             break;
3141 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
3142         case KEY_RESIZE:
3143             wnoutrefresh(stdscr);
3144             break;
3145 #endif
3146         default:
3147             if (cycle_attr(c, &at_code, &attr, my_list, my_size)) {
3148                 slk_attr_set(attr, (NCURSES_COLOR_T) (fg || bg), NULL);
3149                 slk_touch();
3150                 slk_noutrefresh();
3151                 break;
3152             }
3153 #if HAVE_SLK_COLOR
3154             if (cycle_colors(c, &fg, &bg, &pair)) {
3155                 if (use_colors) {
3156                     call_slk_color(fg, bg);
3157                 } else {
3158                     beep();
3159                 }
3160                 break;
3161             }
3162 #endif
3163             beep();
3164             break;
3165         }
3166     } while (!isQuit(c = Getchar()));
3167
3168   done:
3169     slk_clear();
3170     erase();
3171     endwin();
3172 }
3173 #endif
3174 #endif /* SLK_INIT */
3175
3176 static void
3177 show_256_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3178 {
3179     unsigned first = 0;
3180     unsigned last = 255;
3181     unsigned code;
3182     int count;
3183
3184     erase();
3185     attron(A_BOLD);
3186     MvPrintw(0, 20, "Display of Character Codes %#0x to %#0x",
3187              first, last);
3188     attroff(A_BOLD);
3189     refresh();
3190
3191     for (code = first; code <= last; ++code) {
3192         int row = (int) (2 + (code / 16));
3193         int col = (int) (5 * (code % 16));
3194         IGNORE_RC(mvaddch(row, col, colored_chtype(code, attr, pair)));
3195         for (count = 1; count < repeat; ++count) {
3196             addch(colored_chtype(code, attr, pair));
3197         }
3198     }
3199
3200 }
3201
3202 /*
3203  * Show a slice of 32 characters, allowing those to be repeated up to the
3204  * screen's width.
3205  *
3206  * ISO 6429:  codes 0x80 to 0x9f may be control characters that cause the
3207  * terminal to perform functions.  The remaining codes can be graphic.
3208  */
3209 static void
3210 show_upper_chars(int base, int pagesize, int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3211 {
3212     unsigned code;
3213     unsigned first = (unsigned) base;
3214     unsigned last = first + (unsigned) pagesize - 2;
3215     bool C1 = (first == 128);
3216     int reply;
3217
3218     erase();
3219     attron(A_BOLD);
3220     MvPrintw(0, 20, "Display of %s Character Codes %d to %d",
3221              C1 ? "C1" : "GR", first, last);
3222     attroff(A_BOLD);
3223     refresh();
3224
3225     for (code = first; code <= last; code++) {
3226         int count = repeat;
3227         int row = 2 + ((int) (code - first) % (pagesize / 2));
3228         int col = ((int) (code - first) / (pagesize / 2)) * COLS / 2;
3229         char tmp[80];
3230         sprintf(tmp, "%3u (0x%x)", code, code);
3231         MvPrintw(row, col, "%*s: ", COLS / 4, tmp);
3232
3233         do {
3234             if (C1)
3235                 nodelay(stdscr, TRUE);
3236             echochar(colored_chtype(code, attr, pair));
3237             if (C1) {
3238                 /* (yes, this _is_ crude) */
3239                 while ((reply = Getchar()) != ERR) {
3240                     addch(UChar(reply));
3241                     napms(10);
3242                 }
3243                 nodelay(stdscr, FALSE);
3244             }
3245         } while (--count > 0);
3246     }
3247 }
3248
3249 #define PC_COLS 4
3250
3251 static void
3252 show_pc_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3253 {
3254     unsigned code;
3255
3256     erase();
3257     attron(A_BOLD);
3258     MvPrintw(0, 20, "Display of PC Character Codes");
3259     attroff(A_BOLD);
3260     refresh();
3261
3262     for (code = 0; code < 16; ++code) {
3263         MvPrintw(2, (int) code * PC_COLS + 8, "%X", code);
3264     }
3265     for (code = 0; code < 256; code++) {
3266         int count = repeat;
3267         int row = 3 + (int) (code / 16) + (code >= 128);
3268         int col = 8 + (int) (code % 16) * PC_COLS;
3269         if ((code % 16) == 0)
3270             MvPrintw(row, 0, "0x%02x:", code);
3271         move(row, col);
3272         do {
3273             switch (code) {
3274             case '\n':
3275             case '\r':
3276             case '\b':
3277             case '\f':
3278             case '\033':
3279             case 0x9b:
3280                 /*
3281                  * Skip the ones that do not work.
3282                  */
3283                 break;
3284             default:
3285                 addch(colored_chtype(code, A_ALTCHARSET | attr, pair));
3286                 break;
3287             }
3288         } while (--count > 0);
3289     }
3290 }
3291
3292 static void
3293 show_box_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3294 {
3295     (void) repeat;
3296
3297     attr |= (attr_t) COLOR_PAIR(pair);
3298
3299     erase();
3300     attron(A_BOLD);
3301     MvAddStr(0, 20, "Display of the ACS Line-Drawing Set");
3302     attroff(A_BOLD);
3303     refresh();
3304     /* *INDENT-OFF* */
3305     wborder(stdscr,
3306             colored_chtype(ACS_VLINE,    attr, pair),
3307             colored_chtype(ACS_VLINE,    attr, pair),
3308             colored_chtype(ACS_HLINE,    attr, pair),
3309             colored_chtype(ACS_HLINE,    attr, pair),
3310             colored_chtype(ACS_ULCORNER, attr, pair),
3311             colored_chtype(ACS_URCORNER, attr, pair),
3312             colored_chtype(ACS_LLCORNER, attr, pair),
3313             colored_chtype(ACS_LRCORNER, attr, pair));
3314     MvHLine(LINES / 2, 0,        colored_chtype(ACS_HLINE, attr, pair), COLS);
3315     MvVLine(0,         COLS / 2, colored_chtype(ACS_VLINE, attr, pair), LINES);
3316     MvAddCh(0,         COLS / 2, colored_chtype(ACS_TTEE,  attr, pair));
3317     MvAddCh(LINES / 2, COLS / 2, colored_chtype(ACS_PLUS,  attr, pair));
3318     MvAddCh(LINES - 1, COLS / 2, colored_chtype(ACS_BTEE,  attr, pair));
3319     MvAddCh(LINES / 2, 0,        colored_chtype(ACS_LTEE,  attr, pair));
3320     MvAddCh(LINES / 2, COLS - 1, colored_chtype(ACS_RTEE,  attr, pair));
3321     /* *INDENT-ON* */
3322
3323 }
3324
3325 static int
3326 show_1_acs(int n, int repeat, const char *name, chtype code)
3327 {
3328     const int height = 16;
3329     int row = 2 + (n % height);
3330     int col = (n / height) * COLS / 2;
3331
3332     MvPrintw(row, col, "%*s : ", COLS / 4, name);
3333     do {
3334         addch(code);
3335     } while (--repeat > 0);
3336     return n + 1;
3337 }
3338
3339 static void
3340 show_acs_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3341 /* display the ACS character set */
3342 {
3343     int n;
3344
3345 #define BOTH(name) #name, colored_chtype(name, attr, (chtype) pair)
3346
3347     erase();
3348     attron(A_BOLD);
3349     MvAddStr(0, 20, "Display of the ACS Character Set");
3350     attroff(A_BOLD);
3351     refresh();
3352
3353     n = show_1_acs(0, repeat, BOTH(ACS_ULCORNER));
3354     n = show_1_acs(n, repeat, BOTH(ACS_URCORNER));
3355     n = show_1_acs(n, repeat, BOTH(ACS_LLCORNER));
3356     n = show_1_acs(n, repeat, BOTH(ACS_LRCORNER));
3357
3358     n = show_1_acs(n, repeat, BOTH(ACS_LTEE));
3359     n = show_1_acs(n, repeat, BOTH(ACS_RTEE));
3360     n = show_1_acs(n, repeat, BOTH(ACS_TTEE));
3361     n = show_1_acs(n, repeat, BOTH(ACS_BTEE));
3362
3363     n = show_1_acs(n, repeat, BOTH(ACS_HLINE));
3364     n = show_1_acs(n, repeat, BOTH(ACS_VLINE));
3365
3366     /*
3367      * HPUX's ACS definitions are broken here.  Just give up.
3368      */
3369 #if !(defined(__hpux) && !defined(NCURSES_VERSION))
3370     n = show_1_acs(n, repeat, BOTH(ACS_LARROW));
3371     n = show_1_acs(n, repeat, BOTH(ACS_RARROW));
3372     n = show_1_acs(n, repeat, BOTH(ACS_UARROW));
3373     n = show_1_acs(n, repeat, BOTH(ACS_DARROW));
3374
3375     n = show_1_acs(n, repeat, BOTH(ACS_BLOCK));
3376     n = show_1_acs(n, repeat, BOTH(ACS_BOARD));
3377     n = show_1_acs(n, repeat, BOTH(ACS_LANTERN));
3378     n = show_1_acs(n, repeat, BOTH(ACS_BULLET));
3379     n = show_1_acs(n, repeat, BOTH(ACS_CKBOARD));
3380     n = show_1_acs(n, repeat, BOTH(ACS_DEGREE));
3381     n = show_1_acs(n, repeat, BOTH(ACS_DIAMOND));
3382     n = show_1_acs(n, repeat, BOTH(ACS_PLMINUS));
3383     n = show_1_acs(n, repeat, BOTH(ACS_PLUS));
3384
3385     n = show_1_acs(n, repeat, BOTH(ACS_GEQUAL));
3386     n = show_1_acs(n, repeat, BOTH(ACS_NEQUAL));
3387     n = show_1_acs(n, repeat, BOTH(ACS_LEQUAL));
3388
3389     n = show_1_acs(n, repeat, BOTH(ACS_STERLING));
3390     n = show_1_acs(n, repeat, BOTH(ACS_PI));
3391     n = show_1_acs(n, repeat, BOTH(ACS_S1));
3392     n = show_1_acs(n, repeat, BOTH(ACS_S3));
3393     n = show_1_acs(n, repeat, BOTH(ACS_S7));
3394     (void) show_1_acs(n, repeat, BOTH(ACS_S9));
3395 #endif
3396 }
3397
3398 static void
3399 acs_display(void)
3400 {
3401     int c = 'a';
3402     int pagesize = 32;
3403     char *term = getenv("TERM");
3404     const char *pch_kludge = ((term != 0 && strstr(term, "linux"))
3405                               ? "p=PC, "
3406                               : "");
3407     chtype attr = A_NORMAL;
3408     int digit = 0;
3409     int repeat = 1;
3410     int fg = COLOR_BLACK;
3411     int bg = COLOR_BLACK;
3412     unsigned at_code = 0;
3413     NCURSES_PAIRS_T pair = 0;
3414     void (*last_show_acs) (int, attr_t, NCURSES_PAIRS_T) = 0;
3415     ATTR_TBL my_list[SIZEOF(attrs_to_test)];
3416     unsigned my_size = init_attr_list(my_list, termattrs());
3417
3418     do {
3419         switch (c) {
3420         case CTRL('L'):
3421             Repaint();
3422             break;
3423         case 'a':
3424             ToggleAcs(last_show_acs, show_acs_chars);
3425             break;
3426         case 'p':
3427             if (*pch_kludge)
3428                 ToggleAcs(last_show_acs, show_pc_chars);
3429             else
3430                 beep();
3431             break;
3432         case 'w':
3433             if (pagesize == 32) {
3434                 pagesize = 256;
3435             } else {
3436                 pagesize = 32;
3437             }
3438             break;
3439         case 'x':
3440             ToggleAcs(last_show_acs, show_box_chars);
3441             break;
3442         case '0':
3443         case '1':
3444         case '2':
3445         case '3':
3446             digit = (c - '0');
3447             last_show_acs = 0;
3448             break;
3449         case '-':
3450             if (digit > 0) {
3451                 --digit;
3452                 last_show_acs = 0;
3453             } else {
3454                 beep();
3455             }
3456             break;
3457         case '+':
3458             if (digit < 3) {
3459                 ++digit;
3460                 last_show_acs = 0;
3461             } else {
3462                 beep();
3463             }
3464             break;
3465         case '>':
3466             if (repeat < (COLS / 4))
3467                 ++repeat;
3468             break;
3469         case '<':
3470             if (repeat > 1)
3471                 --repeat;
3472             break;
3473         default:
3474             if (cycle_attr(c, &at_code, &attr, my_list, my_size)
3475                 || cycle_colors(c, &fg, &bg, &pair)) {
3476                 break;
3477             } else {
3478                 beep();
3479             }
3480             break;
3481         }
3482         if (pagesize != 32) {
3483             show_256_chars(repeat, attr, pair);
3484         } else if (last_show_acs != 0) {
3485             last_show_acs(repeat, attr, pair);
3486         } else {
3487             show_upper_chars(digit * pagesize + 128, pagesize, repeat, attr, pair);
3488         }
3489
3490         MvPrintw(LINES - 3, 0,
3491                  "Note: ANSI terminals may not display C1 characters.");
3492         MvPrintw(LINES - 2, 0,
3493                  "Select: a=ACS, w=all x=box, %s0=C1, 1-3,+/- non-ASCII, </> repeat, ESC=quit",
3494                  pch_kludge);
3495         if (use_colors) {
3496             MvPrintw(LINES - 1, 0,
3497                      "v/V, f/F, b/B cycle through video attributes (%s) and color %d/%d.",
3498                      my_list[at_code].name,
3499                      fg, bg);
3500         } else {
3501             MvPrintw(LINES - 1, 0,
3502                      "v/V cycles through video attributes (%s).",
3503                      my_list[at_code].name);
3504         }
3505         refresh();
3506     } while (!isQuit(c = Getchar()));
3507
3508     Pause();
3509     erase();
3510     endwin();
3511 }
3512
3513 #if USE_WIDEC_SUPPORT
3514 static cchar_t *
3515 merge_wide_attr(cchar_t *dst, const cchar_t *src, attr_t attr, NCURSES_PAIRS_T pair)
3516 {
3517     int count;
3518
3519     *dst = *src;
3520     do {
3521         TEST_CCHAR(src, count, {
3522             attr |= (test_attrs & A_ALTCHARSET);
3523             setcchar(dst, test_wch, attr, pair, NULL);
3524         }
3525         , {
3526             ;
3527         });
3528     } while (0);
3529     return dst;
3530 }
3531
3532 /*
3533  * Header/legend take up no more than 8 lines, leaving 16 lines on a 24-line
3534  * display.  If there are no repeats, we could normally display 16 lines of 64
3535  * characters (1024 total).  However, taking repeats and double-width cells
3536  * into account, use 256 characters for the page.
3537  */
3538 static void
3539 show_paged_widechars(int base,
3540                      int pagesize,
3541                      int repeat,
3542                      int space,
3543                      attr_t attr,
3544                      NCURSES_PAIRS_T pair)
3545 {
3546     int first = base * pagesize;
3547     int last = first + pagesize - 1;
3548     int per_line = 16;
3549     cchar_t temp;
3550     wchar_t code;
3551     wchar_t codes[10];
3552
3553     erase();
3554     attron(A_BOLD);
3555     MvPrintw(0, 20, "Display of Character Codes %#x to %#x", first, last);
3556     attroff(A_BOLD);
3557
3558     for (code = first; (int) code <= last; code++) {
3559         int row = (2 + ((int) code - first) / per_line);
3560         int col = 5 * ((int) code % per_line);
3561         int count;
3562
3563         memset(&codes, 0, sizeof(codes));
3564         codes[0] = code;
3565         setcchar(&temp, codes, attr, pair, 0);
3566         move(row, col);
3567         if (wcwidth(code) == 0 && code != 0) {
3568             addch((chtype) space |
3569                   (A_REVERSE ^ attr) |
3570                   (attr_t) COLOR_PAIR(pair));
3571         }
3572         add_wch(&temp);
3573         for (count = 1; count < repeat; ++count) {
3574             add_wch(&temp);
3575         }
3576     }
3577 }
3578
3579 static void
3580 show_upper_widechars(int first, int repeat, int space, attr_t attr, NCURSES_PAIRS_T pair)
3581 {
3582     cchar_t temp;
3583     wchar_t code;
3584     int last = first + 31;
3585
3586     erase();
3587     attron(A_BOLD);
3588     MvPrintw(0, 20, "Display of Character Codes %d to %d", first, last);
3589     attroff(A_BOLD);
3590
3591     for (code = first; (int) code <= last; code++) {
3592         int row = 2 + ((code - first) % 16);
3593         int col = ((code - first) / 16) * COLS / 2;
3594         wchar_t codes[10];
3595         char tmp[80];
3596         int count = repeat;
3597         int y, x;
3598
3599         sprintf(tmp, "%3ld (0x%lx)", (long) code, (long) code);
3600         MvPrintw(row, col, "%*s: ", COLS / 4, tmp);
3601
3602         memset(&codes, 0, sizeof(codes));
3603         codes[0] = code;
3604         setcchar(&temp, codes, attr, pair, 0);
3605
3606         do {
3607             /*
3608              * Give non-spacing characters something to combine with.  If we
3609              * don't, they'll bunch up in a heap on the space after the ":".
3610              * Mark them with reverse-video to make them simpler to find on
3611              * the display.
3612              */
3613             if (wcwidth(code) == 0) {
3614                 addch((chtype) space |
3615                       (A_REVERSE ^ attr) |
3616                       (attr_t) COLOR_PAIR(pair));
3617             }
3618             /*
3619              * This uses echo_wchar(), for comparison with the normal 'f'
3620              * test (and to make a test-case for echo_wchar()).  The screen
3621              * may flicker because the erase() at the top of the function
3622              * is met by the builtin refresh() in echo_wchar().
3623              */
3624             echo_wchar(&temp);
3625             /*
3626              * The repeat-count may make text wrap - avoid that.
3627              */
3628             getyx(stdscr, y, x);
3629             (void) y;
3630             if (x >= col + (COLS / 2) - 2)
3631                 break;
3632         } while (--count > 0);
3633     }
3634 }
3635
3636 static int
3637 show_1_wacs(int n, int repeat, const char *name, const cchar_t *code)
3638 {
3639     const int height = 16;
3640     int row = 2 + (n % height);
3641     int col = (n / height) * COLS / 2;
3642
3643     MvPrintw(row, col, "%*s : ", COLS / 4, name);
3644     while (--repeat >= 0) {
3645         add_wch(code);
3646     }
3647     return n + 1;
3648 }
3649
3650 #define MERGE_ATTR(wch) merge_wide_attr(&temp, wch, attr, pair)
3651
3652 static void
3653 show_wacs_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3654 /* display the wide-ACS character set */
3655 {
3656     cchar_t temp;
3657
3658     int n;
3659
3660 /*#define BOTH2(name) #name, &(name) */
3661 #define BOTH2(name) #name, MERGE_ATTR(name)
3662
3663     erase();
3664     attron(A_BOLD);
3665     MvAddStr(0, 20, "Display of the Wide-ACS Character Set");
3666     attroff(A_BOLD);
3667     refresh();
3668
3669     n = show_1_wacs(0, repeat, BOTH2(WACS_ULCORNER));
3670     n = show_1_wacs(n, repeat, BOTH2(WACS_URCORNER));
3671     n = show_1_wacs(n, repeat, BOTH2(WACS_LLCORNER));
3672     n = show_1_wacs(n, repeat, BOTH2(WACS_LRCORNER));
3673
3674     n = show_1_wacs(n, repeat, BOTH2(WACS_LTEE));
3675     n = show_1_wacs(n, repeat, BOTH2(WACS_RTEE));
3676     n = show_1_wacs(n, repeat, BOTH2(WACS_TTEE));
3677     n = show_1_wacs(n, repeat, BOTH2(WACS_BTEE));
3678
3679     n = show_1_wacs(n, repeat, BOTH2(WACS_HLINE));
3680     n = show_1_wacs(n, repeat, BOTH2(WACS_VLINE));
3681
3682     n = show_1_wacs(n, repeat, BOTH2(WACS_LARROW));
3683     n = show_1_wacs(n, repeat, BOTH2(WACS_RARROW));
3684     n = show_1_wacs(n, repeat, BOTH2(WACS_UARROW));
3685     n = show_1_wacs(n, repeat, BOTH2(WACS_DARROW));
3686
3687     n = show_1_wacs(n, repeat, BOTH2(WACS_BLOCK));
3688     n = show_1_wacs(n, repeat, BOTH2(WACS_BOARD));
3689     n = show_1_wacs(n, repeat, BOTH2(WACS_LANTERN));
3690     n = show_1_wacs(n, repeat, BOTH2(WACS_BULLET));
3691     n = show_1_wacs(n, repeat, BOTH2(WACS_CKBOARD));
3692     n = show_1_wacs(n, repeat, BOTH2(WACS_DEGREE));
3693     n = show_1_wacs(n, repeat, BOTH2(WACS_DIAMOND));
3694     n = show_1_wacs(n, repeat, BOTH2(WACS_PLMINUS));
3695     n = show_1_wacs(n, repeat, BOTH2(WACS_PLUS));
3696
3697 #ifdef CURSES_WACS_ARRAY
3698     n = show_1_wacs(n, repeat, BOTH2(WACS_GEQUAL));
3699     n = show_1_wacs(n, repeat, BOTH2(WACS_NEQUAL));
3700     n = show_1_wacs(n, repeat, BOTH2(WACS_LEQUAL));
3701
3702     n = show_1_wacs(n, repeat, BOTH2(WACS_STERLING));
3703     n = show_1_wacs(n, repeat, BOTH2(WACS_PI));
3704     n = show_1_wacs(n, repeat, BOTH2(WACS_S1));
3705     n = show_1_wacs(n, repeat, BOTH2(WACS_S3));
3706     n = show_1_wacs(n, repeat, BOTH2(WACS_S7));
3707     (void) show_1_wacs(n, repeat, BOTH2(WACS_S9));
3708 #endif
3709 }
3710
3711 #ifdef WACS_D_PLUS
3712 static void
3713 show_wacs_chars_double(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3714 /* display the wide-ACS character set */
3715 {
3716     cchar_t temp;
3717
3718     int n;
3719
3720 /*#define BOTH2(name) #name, &(name) */
3721 #define BOTH2(name) #name, MERGE_ATTR(name)
3722
3723     erase();
3724     attron(A_BOLD);
3725     MvAddStr(0, 20, "Display of the Wide-ACS Character Set");
3726     attroff(A_BOLD);
3727     refresh();
3728
3729     n = show_1_wacs(0, repeat, BOTH2(WACS_D_ULCORNER));
3730     n = show_1_wacs(n, repeat, BOTH2(WACS_D_URCORNER));
3731     n = show_1_wacs(n, repeat, BOTH2(WACS_D_LLCORNER));
3732     n = show_1_wacs(n, repeat, BOTH2(WACS_D_LRCORNER));
3733
3734     n = show_1_wacs(n, repeat, BOTH2(WACS_D_LTEE));
3735     n = show_1_wacs(n, repeat, BOTH2(WACS_D_RTEE));
3736     n = show_1_wacs(n, repeat, BOTH2(WACS_D_TTEE));
3737     n = show_1_wacs(n, repeat, BOTH2(WACS_D_BTEE));
3738
3739     n = show_1_wacs(n, repeat, BOTH2(WACS_D_HLINE));
3740     n = show_1_wacs(n, repeat, BOTH2(WACS_D_VLINE));
3741
3742     n = show_1_wacs(n, repeat, BOTH2(WACS_LARROW));
3743     n = show_1_wacs(n, repeat, BOTH2(WACS_RARROW));
3744     n = show_1_wacs(n, repeat, BOTH2(WACS_UARROW));
3745     n = show_1_wacs(n, repeat, BOTH2(WACS_DARROW));
3746
3747     n = show_1_wacs(n, repeat, BOTH2(WACS_BLOCK));
3748     n = show_1_wacs(n, repeat, BOTH2(WACS_BOARD));
3749     n = show_1_wacs(n, repeat, BOTH2(WACS_LANTERN));
3750     n = show_1_wacs(n, repeat, BOTH2(WACS_BULLET));
3751     n = show_1_wacs(n, repeat, BOTH2(WACS_CKBOARD));
3752     n = show_1_wacs(n, repeat, BOTH2(WACS_DEGREE));
3753     n = show_1_wacs(n, repeat, BOTH2(WACS_DIAMOND));
3754     n = show_1_wacs(n, repeat, BOTH2(WACS_PLMINUS));
3755     n = show_1_wacs(n, repeat, BOTH2(WACS_PLUS));
3756
3757 #ifdef CURSES_WACS_ARRAY
3758     n = show_1_wacs(n, repeat, BOTH2(WACS_GEQUAL));
3759     n = show_1_wacs(n, repeat, BOTH2(WACS_NEQUAL));
3760     n = show_1_wacs(n, repeat, BOTH2(WACS_LEQUAL));
3761
3762     n = show_1_wacs(n, repeat, BOTH2(WACS_STERLING));
3763     n = show_1_wacs(n, repeat, BOTH2(WACS_PI));
3764     n = show_1_wacs(n, repeat, BOTH2(WACS_S1));
3765     n = show_1_wacs(n, repeat, BOTH2(WACS_S3));
3766     n = show_1_wacs(n, repeat, BOTH2(WACS_S7));
3767     (void) show_1_wacs(n, repeat, BOTH2(WACS_S9));
3768 #endif
3769 }
3770 #endif
3771
3772 #ifdef WACS_T_PLUS
3773 static void
3774 show_wacs_chars_thick(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3775 /* display the wide-ACS character set */
3776 {
3777     cchar_t temp;
3778
3779     int n;
3780
3781 /*#define BOTH2(name) #name, &(name) */
3782 #define BOTH2(name) #name, MERGE_ATTR(name)
3783
3784     erase();
3785     attron(A_BOLD);
3786     MvAddStr(0, 20, "Display of the Wide-ACS Character Set");
3787     attroff(A_BOLD);
3788     refresh();
3789
3790     n = show_1_wacs(0, repeat, BOTH2(WACS_T_ULCORNER));
3791     n = show_1_wacs(n, repeat, BOTH2(WACS_T_URCORNER));
3792     n = show_1_wacs(n, repeat, BOTH2(WACS_T_LLCORNER));
3793     n = show_1_wacs(n, repeat, BOTH2(WACS_T_LRCORNER));
3794
3795     n = show_1_wacs(n, repeat, BOTH2(WACS_T_LTEE));
3796     n = show_1_wacs(n, repeat, BOTH2(WACS_T_RTEE));
3797     n = show_1_wacs(n, repeat, BOTH2(WACS_T_TTEE));
3798     n = show_1_wacs(n, repeat, BOTH2(WACS_T_BTEE));
3799
3800     n = show_1_wacs(n, repeat, BOTH2(WACS_T_HLINE));
3801     n = show_1_wacs(n, repeat, BOTH2(WACS_T_VLINE));
3802
3803     n = show_1_wacs(n, repeat, BOTH2(WACS_LARROW));
3804     n = show_1_wacs(n, repeat, BOTH2(WACS_RARROW));
3805     n = show_1_wacs(n, repeat, BOTH2(WACS_UARROW));
3806     n = show_1_wacs(n, repeat, BOTH2(WACS_DARROW));
3807
3808     n = show_1_wacs(n, repeat, BOTH2(WACS_BLOCK));
3809     n = show_1_wacs(n, repeat, BOTH2(WACS_BOARD));
3810     n = show_1_wacs(n, repeat, BOTH2(WACS_LANTERN));
3811     n = show_1_wacs(n, repeat, BOTH2(WACS_BULLET));
3812     n = show_1_wacs(n, repeat, BOTH2(WACS_CKBOARD));
3813     n = show_1_wacs(n, repeat, BOTH2(WACS_DEGREE));
3814     n = show_1_wacs(n, repeat, BOTH2(WACS_DIAMOND));
3815     n = show_1_wacs(n, repeat, BOTH2(WACS_PLMINUS));
3816     n = show_1_wacs(n, repeat, BOTH2(WACS_PLUS));
3817
3818 #ifdef CURSES_WACS_ARRAY
3819     n = show_1_wacs(n, repeat, BOTH2(WACS_GEQUAL));
3820     n = show_1_wacs(n, repeat, BOTH2(WACS_NEQUAL));
3821     n = show_1_wacs(n, repeat, BOTH2(WACS_LEQUAL));
3822
3823     n = show_1_wacs(n, repeat, BOTH2(WACS_STERLING));
3824     n = show_1_wacs(n, repeat, BOTH2(WACS_PI));
3825     n = show_1_wacs(n, repeat, BOTH2(WACS_S1));
3826     n = show_1_wacs(n, repeat, BOTH2(WACS_S3));
3827     n = show_1_wacs(n, repeat, BOTH2(WACS_S7));
3828     (void) show_1_wacs(n, repeat, BOTH2(WACS_S9));
3829 #endif
3830 }
3831 #endif
3832
3833 #undef MERGE_ATTR
3834
3835 #define MERGE_ATTR(n,wch) merge_wide_attr(&temp[n], wch, attr, pair)
3836
3837 static void
3838 show_wbox_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3839 {
3840     cchar_t temp[8];
3841
3842     (void) repeat;
3843     erase();
3844     attron(A_BOLD);
3845     MvAddStr(0, 20, "Display of the Wide-ACS Line-Drawing Set");
3846     attroff(A_BOLD);
3847     refresh();
3848
3849     wborder_set(stdscr,
3850                 MERGE_ATTR(0, WACS_VLINE),
3851                 MERGE_ATTR(1, WACS_VLINE),
3852                 MERGE_ATTR(2, WACS_HLINE),
3853                 MERGE_ATTR(3, WACS_HLINE),
3854                 MERGE_ATTR(4, WACS_ULCORNER),
3855                 MERGE_ATTR(5, WACS_URCORNER),
3856                 MERGE_ATTR(6, WACS_LLCORNER),
3857                 MERGE_ATTR(7, WACS_LRCORNER));
3858     /* *INDENT-OFF* */
3859     (void) mvhline_set(LINES / 2, 0,        MERGE_ATTR(0, WACS_HLINE), COLS);
3860     (void) mvvline_set(0,         COLS / 2, MERGE_ATTR(0, WACS_VLINE), LINES);
3861     (void) mvadd_wch(0,           COLS / 2, MERGE_ATTR(0, WACS_TTEE));
3862     (void) mvadd_wch(LINES / 2,   COLS / 2, MERGE_ATTR(0, WACS_PLUS));
3863     (void) mvadd_wch(LINES - 1,   COLS / 2, MERGE_ATTR(0, WACS_BTEE));
3864     (void) mvadd_wch(LINES / 2,   0,        MERGE_ATTR(0, WACS_LTEE));
3865     (void) mvadd_wch(LINES / 2,   COLS - 1, MERGE_ATTR(0, WACS_RTEE));
3866     /* *INDENT-ON* */
3867
3868 }
3869
3870 #undef MERGE_ATTR
3871
3872 static int
3873 show_2_wacs(int n, const char *name, const char *code, attr_t attr, NCURSES_PAIRS_T pair)
3874 {
3875     const int height = 16;
3876     int row = 2 + (n % height);
3877     int col = (n / height) * COLS / 2;
3878     char temp[80];
3879
3880     MvPrintw(row, col, "%*s : ", COLS / 4, name);
3881     (void) attr_set(attr, pair, 0);
3882     addstr(strncpy(temp, code, 20));
3883     (void) attr_set(A_NORMAL, 0, 0);
3884     return n + 1;
3885 }
3886
3887 #define SHOW_UTF8(n, name, code) show_2_wacs(n, name, code, attr, pair)
3888
3889 static void
3890 show_utf8_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3891 {
3892     int n;
3893
3894     (void) repeat;
3895     erase();
3896     attron(A_BOLD);
3897     MvAddStr(0, 20, "Display of the Wide-ACS Character Set");
3898     attroff(A_BOLD);
3899     refresh();
3900     /* *INDENT-OFF* */
3901     n = SHOW_UTF8(0, "WACS_ULCORNER",   "\342\224\214");
3902     n = SHOW_UTF8(n, "WACS_URCORNER",   "\342\224\220");
3903     n = SHOW_UTF8(n, "WACS_LLCORNER",   "\342\224\224");
3904     n = SHOW_UTF8(n, "WACS_LRCORNER",   "\342\224\230");
3905
3906     n = SHOW_UTF8(n, "WACS_LTEE",       "\342\224\234");
3907     n = SHOW_UTF8(n, "WACS_RTEE",       "\342\224\244");
3908     n = SHOW_UTF8(n, "WACS_TTEE",       "\342\224\254");
3909     n = SHOW_UTF8(n, "WACS_BTEE",       "\342\224\264");
3910
3911     n = SHOW_UTF8(n, "WACS_HLINE",      "\342\224\200");
3912     n = SHOW_UTF8(n, "WACS_VLINE",      "\342\224\202");
3913
3914     n = SHOW_UTF8(n, "WACS_LARROW",     "\342\206\220");
3915     n = SHOW_UTF8(n, "WACS_RARROW",     "\342\206\222");
3916     n = SHOW_UTF8(n, "WACS_UARROW",     "\342\206\221");
3917     n = SHOW_UTF8(n, "WACS_DARROW",     "\342\206\223");
3918
3919     n = SHOW_UTF8(n, "WACS_BLOCK",      "\342\226\256");
3920     n = SHOW_UTF8(n, "WACS_BOARD",      "\342\226\222");
3921     n = SHOW_UTF8(n, "WACS_LANTERN",    "\342\230\203");
3922     n = SHOW_UTF8(n, "WACS_BULLET",     "\302\267");
3923     n = SHOW_UTF8(n, "WACS_CKBOARD",    "\342\226\222");
3924     n = SHOW_UTF8(n, "WACS_DEGREE",     "\302\260");
3925     n = SHOW_UTF8(n, "WACS_DIAMOND",    "\342\227\206");
3926     n = SHOW_UTF8(n, "WACS_PLMINUS",    "\302\261");
3927     n = SHOW_UTF8(n, "WACS_PLUS",       "\342\224\274");
3928     n = SHOW_UTF8(n, "WACS_GEQUAL",     "\342\211\245");
3929     n = SHOW_UTF8(n, "WACS_NEQUAL",     "\342\211\240");
3930     n = SHOW_UTF8(n, "WACS_LEQUAL",     "\342\211\244");
3931
3932     n = SHOW_UTF8(n, "WACS_STERLING",   "\302\243");
3933     n = SHOW_UTF8(n, "WACS_PI",         "\317\200");
3934     n = SHOW_UTF8(n, "WACS_S1",         "\342\216\272");
3935     n = SHOW_UTF8(n, "WACS_S3",         "\342\216\273");
3936     n = SHOW_UTF8(n, "WACS_S7",         "\342\216\274");
3937     (void) SHOW_UTF8(n, "WACS_S9",      "\342\216\275");
3938     /* *INDENT-ON* */
3939
3940 }
3941
3942 /* display the wide-ACS character set */
3943 static void
3944 wide_acs_display(void)
3945 {
3946     int c = 'a';
3947     int digit = 0;
3948     int repeat = 1;
3949     int space = ' ';
3950     int pagesize = 32;
3951     chtype attr = A_NORMAL;
3952     int fg = COLOR_BLACK;
3953     int bg = COLOR_BLACK;
3954     unsigned at_code = 0;
3955     NCURSES_PAIRS_T pair = 0;
3956     void (*last_show_wacs) (int, attr_t, NCURSES_PAIRS_T) = 0;
3957     ATTR_TBL my_list[SIZEOF(attrs_to_test)];
3958     unsigned my_size = init_attr_list(my_list, term_attrs());
3959
3960     do {
3961         switch (c) {
3962         case CTRL('L'):
3963             Repaint();
3964             break;
3965         case 'a':
3966             ToggleAcs(last_show_wacs, show_wacs_chars);
3967             break;
3968 #ifdef WACS_D_PLUS
3969         case 'd':
3970             ToggleAcs(last_show_wacs, show_wacs_chars_double);
3971             break;
3972 #endif
3973 #ifdef WACS_T_PLUS
3974         case 't':
3975             ToggleAcs(last_show_wacs, show_wacs_chars_thick);
3976             break;
3977 #endif
3978         case 'w':
3979             if (pagesize == 32) {
3980                 pagesize = 256;
3981             } else {
3982                 pagesize = 32;
3983             }
3984             break;
3985         case 'x':
3986             ToggleAcs(last_show_wacs, show_wbox_chars);
3987             break;
3988         case 'u':
3989             ToggleAcs(last_show_wacs, show_utf8_chars);
3990             break;
3991         default:
3992             if (c < 256 && isdigit(c)) {
3993                 digit = (c - '0');
3994                 last_show_wacs = 0;
3995             } else if (c == '+') {
3996                 ++digit;
3997                 last_show_wacs = 0;
3998             } else if (c == '-' && digit > 0) {
3999                 --digit;
4000                 last_show_wacs = 0;
4001             } else if (c == '>' && repeat < (COLS / 4)) {
4002                 ++repeat;
4003             } else if (c == '<' && repeat > 1) {
4004                 --repeat;
4005             } else if (c == '_') {
4006                 space = (space == ' ') ? '_' : ' ';
4007                 last_show_wacs = 0;
4008             } else if (cycle_attr(c, &at_code, &attr, my_list, my_size)
4009                        || cycle_colors(c, &fg, &bg, &pair)) {
4010                 if (last_show_wacs != 0)
4011                     break;
4012             } else {
4013                 beep();
4014                 break;
4015             }
4016             break;
4017         }
4018         if (pagesize != 32) {
4019             show_paged_widechars(digit, pagesize, repeat, space, attr, pair);
4020         } else if (last_show_wacs != 0) {
4021             last_show_wacs(repeat, attr, pair);
4022         } else {
4023             show_upper_widechars(digit * 32 + 128, repeat, space, attr, pair);
4024         }
4025
4026         MvPrintw(LINES - 4, 0,
4027                  "Select: a/d/t WACS, w=all x=box, u UTF-8, ^L repaint");
4028         MvPrintw(LINES - 3, 2,
4029                  "0-9,+/- non-ASCII, </> repeat, _ space, ESC=quit");
4030         if (use_colors) {
4031             MvPrintw(LINES - 2, 2,
4032                      "v/V, f/F, b/B cycle through video attributes (%s) and color %d/%d.",
4033                      my_list[at_code].name,
4034                      fg, bg);
4035         } else {
4036             MvPrintw(LINES - 2, 2,
4037                      "v/V cycles through video attributes (%s).",
4038                      my_list[at_code].name);
4039         }
4040         refresh();
4041     } while (!isQuit(c = Getchar()));
4042
4043     Pause();
4044     erase();
4045     endwin();
4046 }
4047
4048 #endif
4049
4050 /*
4051  * Graphic-rendition test (adapted from vttest)
4052  */
4053 static void
4054 test_sgr_attributes(void)
4055 {
4056     int pass;
4057
4058     for (pass = 0; pass < 2; pass++) {
4059         chtype normal = ((pass == 0 ? A_NORMAL : A_REVERSE)) | BLANK;
4060
4061         /* Use non-default colors if possible to exercise bce a little */
4062         if (use_colors) {
4063             init_pair(1, COLOR_WHITE, COLOR_BLUE);
4064             normal |= (chtype) COLOR_PAIR(1);
4065         }
4066         bkgdset(normal);
4067         erase();
4068         MvPrintw(1, 20, "Graphic rendition test pattern:");
4069
4070         MvPrintw(4, 1, "vanilla");
4071
4072 #define set_sgr(mask) bkgdset((normal^(mask)));
4073         set_sgr(A_BOLD);
4074         MvPrintw(4, 40, "bold");
4075
4076         set_sgr(A_UNDERLINE);
4077         MvPrintw(6, 6, "underline");
4078
4079         set_sgr(A_BOLD | A_UNDERLINE);
4080         MvPrintw(6, 45, "bold underline");
4081
4082         set_sgr(A_BLINK);
4083         MvPrintw(8, 1, "blink");
4084
4085         set_sgr(A_BLINK | A_BOLD);
4086         MvPrintw(8, 40, "bold blink");
4087
4088         set_sgr(A_UNDERLINE | A_BLINK);
4089         MvPrintw(10, 6, "underline blink");
4090
4091         set_sgr(A_BOLD | A_UNDERLINE | A_BLINK);
4092         MvPrintw(10, 45, "bold underline blink");
4093
4094         set_sgr(A_REVERSE);
4095         MvPrintw(12, 1, "negative");
4096
4097         set_sgr(A_BOLD | A_REVERSE);
4098         MvPrintw(12, 40, "bold negative");
4099
4100         set_sgr(A_UNDERLINE | A_REVERSE);
4101         MvPrintw(14, 6, "underline negative");
4102
4103         set_sgr(A_BOLD | A_UNDERLINE | A_REVERSE);
4104         MvPrintw(14, 45, "bold underline negative");
4105
4106         set_sgr(A_BLINK | A_REVERSE);
4107         MvPrintw(16, 1, "blink negative");
4108
4109         set_sgr(A_BOLD | A_BLINK | A_REVERSE);
4110         MvPrintw(16, 40, "bold blink negative");
4111
4112         set_sgr(A_UNDERLINE | A_BLINK | A_REVERSE);
4113         MvPrintw(18, 6, "underline blink negative");
4114
4115         set_sgr(A_BOLD | A_UNDERLINE | A_BLINK | A_REVERSE);
4116         MvPrintw(18, 45, "bold underline blink negative");
4117
4118         bkgdset(normal);
4119         MvPrintw(LINES - 2, 1, "%s background. ", pass == 0 ? "Dark" :
4120                  "Light");
4121         clrtoeol();
4122         Pause();
4123     }
4124
4125     bkgdset(A_NORMAL | BLANK);
4126     erase();
4127     endwin();
4128 }
4129
4130 /****************************************************************************
4131  *
4132  * Windows and scrolling tester.
4133  *
4134  ****************************************************************************/
4135
4136 #define BOTLINES        4       /* number of line stolen from screen bottom */
4137
4138 typedef struct {
4139     int y, x;
4140 } pair;
4141
4142 #define FRAME struct frame
4143 FRAME
4144 {
4145     FRAME *next, *last;
4146     bool do_scroll;
4147     bool do_keypad;
4148     WINDOW *wind;
4149 };
4150
4151 #if defined(NCURSES_VERSION)
4152 #if (NCURSES_VERSION_PATCH < 20070331) && NCURSES_EXT_FUNCS
4153 #define is_keypad(win)   (win)->_use_keypad
4154 #define is_scrollok(win) (win)->_scroll
4155 #elif !defined(is_keypad)
4156 #define is_keypad(win)   FALSE
4157 #define is_scrollok(win) FALSE
4158 #endif
4159 #else
4160 #define is_keypad(win)   FALSE
4161 #define is_scrollok(win) FALSE
4162 #endif
4163
4164 static WINDOW *
4165 frame_win(FRAME * curp)
4166 {
4167     return (curp != 0) ? curp->wind : stdscr;
4168 }
4169
4170 /* We need to know if these flags are actually set, so don't look in FRAME.
4171  * These names are known to work with SVr4 curses as well as ncurses.  The
4172  * _use_keypad name does not work with Solaris 8.
4173  */
4174 static bool
4175 HaveKeypad(FRAME * curp)
4176 {
4177     WINDOW *win = frame_win(curp);
4178     (void) win;
4179     return is_keypad(win);
4180 }
4181
4182 static bool
4183 HaveScroll(FRAME * curp)
4184 {
4185     WINDOW *win = frame_win(curp);
4186     (void) win;
4187     return is_scrollok(win);
4188 }
4189
4190 static void
4191 newwin_legend(FRAME * curp)
4192 {
4193     static const struct {
4194         const char *msg;
4195         int code;
4196     } legend[] = {
4197         {
4198             "^C = create window", 0
4199         },
4200         {
4201             "^N = next window", 0
4202         },
4203         {
4204             "^P = previous window", 0
4205         },
4206         {
4207             "^F = scroll forward", 0
4208         },
4209         {
4210             "^B = scroll backward", 0
4211         },
4212         {
4213             "^K = keypad(%s)", 1
4214         },
4215         {
4216             "^S = scrollok(%s)", 2
4217         },
4218         {
4219             "^W = save window to file", 0
4220         },
4221         {
4222             "^R = restore window", 0
4223         },
4224 #if HAVE_WRESIZE
4225         {
4226             "^X = resize", 0
4227         },
4228 #endif
4229         {
4230             "^Q%s = exit", 3
4231         }
4232     };
4233     size_t n;
4234     int x;
4235     bool do_keypad = HaveKeypad(curp);
4236     bool do_scroll = HaveScroll(curp);
4237     char buf[BUFSIZ];
4238
4239     move(LINES - 4, 0);
4240     for (n = 0; n < SIZEOF(legend); n++) {
4241         switch (legend[n].code) {
4242         default:
4243             strcpy(buf, legend[n].msg);
4244             break;
4245         case 1:
4246             sprintf(buf, legend[n].msg, do_keypad ? "yes" : "no");
4247             break;
4248         case 2:
4249             sprintf(buf, legend[n].msg, do_scroll ? "yes" : "no");
4250             break;
4251         case 3:
4252             sprintf(buf, legend[n].msg, do_keypad ? "/ESC" : "");
4253             break;
4254         }
4255         x = getcurx(stdscr);
4256         addstr((COLS < (x + 3 + (int) strlen(buf))) ? "\n" : (n ? ", " : ""));
4257         addstr(buf);
4258     }
4259     clrtoeol();
4260 }
4261
4262 static void
4263 transient(FRAME * curp, NCURSES_CONST char *msg)
4264 {
4265     newwin_legend(curp);
4266     if (msg) {
4267         MvAddStr(LINES - 1, 0, msg);
4268         refresh();
4269         napms(1000);
4270     }
4271
4272     move(LINES - 1, 0);
4273     printw("%s characters are echoed, window should %sscroll.",
4274            HaveKeypad(curp) ? "Non-arrow" : "All other",
4275            HaveScroll(curp) ? "" : "not ");
4276     clrtoeol();
4277 }
4278
4279 static void
4280 newwin_report(FRAME * curp)
4281 /* report on the cursor's current position, then restore it */
4282 {
4283     WINDOW *win = frame_win(curp);
4284     int y, x;
4285
4286     if (win != stdscr)
4287         transient(curp, (char *) 0);
4288     getyx(win, y, x);
4289     move(LINES - 1, COLS - 17);
4290     printw("Y = %2d X = %2d", y, x);
4291     if (win != stdscr)
4292         refresh();
4293     else
4294         wmove(win, y, x);
4295 }
4296
4297 static pair *
4298 selectcell(int uli, int ulj, int lri, int lrj)
4299 /* arrows keys move cursor, return location at current on non-arrow key */
4300 {
4301     static pair res;            /* result cell */
4302     int si = lri - uli + 1;     /* depth of the select area */
4303     int sj = lrj - ulj + 1;     /* width of the select area */
4304     int i = 0, j = 0;           /* offsets into the select area */
4305
4306     res.y = uli;
4307     res.x = ulj;
4308     for (;;) {
4309         move(uli + i, ulj + j);
4310         newwin_report((FRAME *) 0);
4311
4312         switch (Getchar()) {
4313         case KEY_UP:
4314             i += si - 1;
4315             break;
4316         case KEY_DOWN:
4317             i++;
4318             break;
4319         case KEY_LEFT:
4320             j += sj - 1;
4321             break;
4322         case KEY_RIGHT:
4323             j++;
4324             break;
4325         case case_QUIT:
4326             return ((pair *) 0);
4327 #ifdef NCURSES_MOUSE_VERSION
4328         case KEY_MOUSE:
4329             {
4330                 MEVENT event;
4331
4332                 getmouse(&event);
4333                 if (event.y > uli && event.x > ulj) {
4334                     i = event.y - uli;
4335                     j = event.x - ulj;
4336                 } else {
4337                     beep();
4338                     break;
4339                 }
4340             }
4341             /* FALLTHRU */
4342 #endif
4343         default:
4344             res.y = uli + i;
4345             res.x = ulj + j;
4346             return (&res);
4347         }
4348         i %= si;
4349         j %= sj;
4350     }
4351 }
4352
4353 static void
4354 outerbox(pair ul, pair lr, bool onoff)
4355 /* draw or erase a box *outside* the given pair of corners */
4356 {
4357     MvAddCh(ul.y - 1, lr.x - 1, onoff ? ACS_ULCORNER : ' ');
4358     MvAddCh(ul.y - 1, lr.x + 1, onoff ? ACS_URCORNER : ' ');
4359     MvAddCh(lr.y + 1, lr.x + 1, onoff ? ACS_LRCORNER : ' ');
4360     MvAddCh(lr.y + 1, ul.x - 1, onoff ? ACS_LLCORNER : ' ');
4361     move(ul.y - 1, ul.x);
4362     hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
4363     move(ul.y, ul.x - 1);
4364     vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
4365     move(lr.y + 1, ul.x);
4366     hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
4367     move(ul.y, lr.x + 1);
4368     vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
4369 }
4370
4371 static WINDOW *
4372 getwindow(void)
4373 /* Ask user for a window definition */
4374 {
4375     WINDOW *rwindow;
4376     pair ul, lr, *tmp;
4377
4378     move(0, 0);
4379     clrtoeol();
4380     addstr("Use arrows to move cursor, anything else to mark corner 1");
4381     refresh();
4382     if ((tmp = selectcell(2, 1, LINES - BOTLINES - 2, COLS - 2)) == (pair *) 0)
4383         return ((WINDOW *) 0);
4384     memcpy(&ul, tmp, sizeof(pair));
4385     MvAddCh(ul.y - 1, ul.x - 1, ACS_ULCORNER);
4386     move(0, 0);
4387     clrtoeol();
4388     addstr("Use arrows to move cursor, anything else to mark corner 2");
4389     refresh();
4390     if ((tmp = selectcell(ul.y, ul.x, LINES - BOTLINES - 2, COLS - 2)) ==
4391         (pair *) 0)
4392         return ((WINDOW *) 0);
4393     memcpy(&lr, tmp, sizeof(pair));
4394
4395     rwindow = subwin(stdscr, lr.y - ul.y + 1, lr.x - ul.x + 1, ul.y, ul.x);
4396
4397     outerbox(ul, lr, TRUE);
4398     refresh();
4399
4400     if (rwindow != 0)
4401         wrefresh(rwindow);
4402
4403     move(0, 0);
4404     clrtoeol();
4405     return (rwindow);
4406 }
4407
4408 static void
4409 newwin_move(FRAME * curp, int dy, int dx)
4410 {
4411     WINDOW *win = frame_win(curp);
4412     int cur_y, cur_x;
4413     int max_y, max_x;
4414
4415     getyx(win, cur_y, cur_x);
4416     getmaxyx(win, max_y, max_x);
4417     if ((cur_x += dx) < 0)
4418         cur_x = 0;
4419     else if (cur_x >= max_x)
4420         cur_x = max_x - 1;
4421     if ((cur_y += dy) < 0)
4422         cur_y = 0;
4423     else if (cur_y >= max_y)
4424         cur_y = max_y - 1;
4425     wmove(win, cur_y, cur_x);
4426 }
4427
4428 static FRAME *
4429 delete_framed(FRAME * fp, bool showit)
4430 {
4431     FRAME *np = 0;
4432
4433     if (fp != 0) {
4434         fp->last->next = fp->next;
4435         fp->next->last = fp->last;
4436
4437         if (showit) {
4438             werase(fp->wind);
4439             wrefresh(fp->wind);
4440         }
4441         delwin(fp->wind);
4442
4443         np = (fp == fp->next) ? 0 : fp->next;
4444         free(fp);
4445     }
4446     return np;
4447 }
4448
4449 static void
4450 acs_and_scroll(void)
4451 /* Demonstrate windows */
4452 {
4453     int c;
4454     FRAME *current = (FRAME *) 0, *neww;
4455     WINDOW *usescr;
4456 #if HAVE_PUTWIN && HAVE_GETWIN
4457     FILE *fp;
4458 #endif
4459
4460 #define DUMPFILE        "screendump"
4461
4462 #ifdef NCURSES_MOUSE_VERSION
4463     mousemask(BUTTON1_CLICKED, (mmask_t *) 0);
4464 #endif
4465     c = CTRL('C');
4466     raw();
4467     do {
4468         transient((FRAME *) 0, (char *) 0);
4469         switch (c) {
4470         case CTRL('C'):
4471             if ((neww = typeCalloc(FRAME, (size_t) 1)) == 0) {
4472                 failed("acs_and_scroll");
4473                 goto breakout;
4474             }
4475             if ((neww->wind = getwindow()) == (WINDOW *) 0) {
4476                 failed("acs_and_scroll");
4477                 free(neww);
4478                 goto breakout;
4479             }
4480
4481             if (current == 0) { /* First element,  */
4482                 neww->next = neww;      /*   so point it at itself */
4483                 neww->last = neww;
4484             } else {
4485                 neww->next = current->next;
4486                 neww->last = current;
4487                 neww->last->next = neww;
4488                 neww->next->last = neww;
4489             }