ncurses 6.0 - patch 20151101
[ncurses.git] / test / ncurses.c
1 /****************************************************************************
2  * Copyright (c) 1998-2014,2015 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.423 2015/10/31 19:53:06 tom Exp $
44
45 ***************************************************************************/
46
47 #include <test.priv.h>
48
49 #ifdef __hpux
50 #undef mvwdelch                 /* HPUX 11.23 macro will not compile */
51 #endif
52
53 #if HAVE_GETTIMEOFDAY
54 #if HAVE_SYS_TIME_H && HAVE_SYS_TIME_SELECT
55 #include <sys/time.h>
56 #endif
57 #if HAVE_SYS_SELECT_H
58 #include <sys/select.h>
59 #endif
60 #endif
61
62 #if USE_LIBPANEL
63 #include <panel.h>
64 #endif
65
66 #if USE_LIBMENU
67 #include <menu.h>
68 #endif
69
70 #if USE_LIBFORM
71 #include <form.h>
72 #endif
73
74 #ifdef NCURSES_VERSION
75
76 #define NCURSES_CONST_PARAM const void
77
78 #ifdef TRACE
79 static unsigned save_trace = TRACE_ORDINARY | TRACE_ICALLS | TRACE_CALLS;
80 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 (wchar_t) (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++ = (wchar_t) 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 | REPORT_MOUSE_POSITION, (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 COLS_PRE_ATTRS 5
1259 #define COLS_AFT_ATTRS 15
1260 #define COL_ATTRSTRING (COLS_PRE_ATTRS + 17)
1261 #define LEN_ATTRSTRING (COLS - (COL_ATTRSTRING + COLS_AFT_ATTRS))
1262 #define MAX_ATTRSTRING (ATTRSTRING_END + 1 - ATTRSTRING_1ST)
1263
1264 static char attr_test_string[MAX_ATTRSTRING + 1];
1265
1266 static void
1267 attr_legend(WINDOW *helpwin)
1268 {
1269     int row = 1;
1270     int col = 1;
1271
1272     MvWPrintw(helpwin, row++, col,
1273               "ESC to exit.");
1274     MvWPrintw(helpwin, row++, col,
1275               "^L repaints.");
1276     ++row;
1277     MvWPrintw(helpwin, row++, col,
1278               "Modify the test strings:");
1279     MvWPrintw(helpwin, row++, col,
1280               "  A digit sets gaps on each side of displayed attributes");
1281     MvWPrintw(helpwin, row++, col,
1282               "  </> shifts the text left/right. ");
1283     ++row;
1284     MvWPrintw(helpwin, row++, col,
1285               "Toggles:");
1286     if (use_colors) {
1287         MvWPrintw(helpwin, row++, col,
1288                   "  f/F/b/F toggle foreground/background background color");
1289         MvWPrintw(helpwin, row++, col,
1290                   "  t/T     toggle text/background color attribute");
1291     }
1292     MvWPrintw(helpwin, row++, col,
1293               "  a/A     toggle ACS (alternate character set) mapping");
1294     MvWPrintw(helpwin, row, col,
1295               "  v/V     toggle video attribute to combine with each line");
1296 #if USE_WIDEC_SUPPORT
1297     MvWPrintw(helpwin, row, col,
1298               "  w/W     toggle normal/wide (double-width) test-characters");
1299 #endif
1300 }
1301
1302 static void
1303 show_color_attr(int fg, int bg, int tx)
1304 {
1305     if (use_colors) {
1306         printw("  Colors (fg %d, bg %d", fg, bg);
1307         if (tx >= 0)
1308             printw(", text %d", tx);
1309         printw("),");
1310     }
1311 }
1312
1313 static bool
1314 cycle_color_attr(int ch, NCURSES_COLOR_T *fg, NCURSES_COLOR_T *bg, NCURSES_COLOR_T *tx)
1315 {
1316     bool error = FALSE;
1317
1318     if (use_colors) {
1319         switch (ch) {
1320         case 'f':
1321             *fg = (NCURSES_COLOR_T) (*fg + 1);
1322             break;
1323         case 'F':
1324             *fg = (NCURSES_COLOR_T) (*fg - 1);
1325             break;
1326         case 'b':
1327             *bg = (NCURSES_COLOR_T) (*bg + 1);
1328             break;
1329         case 'B':
1330             *bg = (NCURSES_COLOR_T) (*bg - 1);
1331             break;
1332         case 't':
1333             *tx = (NCURSES_COLOR_T) (*tx + 1);
1334             break;
1335         case 'T':
1336             *tx = (NCURSES_COLOR_T) (*tx - 1);
1337             break;
1338         default:
1339             beep();
1340             error = TRUE;
1341             break;
1342         }
1343         if (*fg >= COLORS)
1344             *fg = (NCURSES_COLOR_T) min_colors;
1345         if (*fg < min_colors)
1346             *fg = (NCURSES_COLOR_T) (COLORS - 1);
1347         if (*bg >= COLORS)
1348             *bg = (NCURSES_COLOR_T) min_colors;
1349         if (*bg < min_colors)
1350             *bg = (NCURSES_COLOR_T) (COLORS - 1);
1351         if (*tx >= COLORS)
1352             *tx = -1;
1353         if (*tx < -1)
1354             *tx = (NCURSES_COLOR_T) (COLORS - 1);
1355     } else {
1356         beep();
1357         error = TRUE;
1358     }
1359     return error;
1360 }
1361
1362 static void
1363 adjust_attr_string(int adjust)
1364 {
1365     char save = attr_test_string[0];
1366     int first = ((int) UChar(save)) + adjust;
1367     int j, k;
1368
1369     if (first >= ATTRSTRING_1ST) {
1370         for (j = 0, k = first; j < MAX_ATTRSTRING; ++j, ++k) {
1371             if (k > ATTRSTRING_END)
1372                 break;
1373             attr_test_string[j] = (char) k;
1374             if (((k + 1 - first) % 5) == 0) {
1375                 if (++j >= MAX_ATTRSTRING)
1376                     break;
1377                 attr_test_string[j] = ' ';
1378             }
1379         }
1380         if ((LEN_ATTRSTRING - j) > 5) {
1381             attr_test_string[0] = save;
1382             adjust_attr_string(adjust - 1);
1383         } else {
1384             while (j < MAX_ATTRSTRING)
1385                 attr_test_string[j++] = ' ';
1386             attr_test_string[j] = '\0';
1387         }
1388     }
1389 }
1390
1391 /*
1392  * Prefer the right-end of the string for starting, since that maps to the
1393  * VT100 line-drawing.
1394  */
1395 static int
1396 default_attr_string(void)
1397 {
1398     int result = (ATTRSTRING_END - LEN_ATTRSTRING);
1399     result += (LEN_ATTRSTRING / 5);
1400     if (result < ATTRSTRING_1ST)
1401         result = ATTRSTRING_1ST;
1402     return result;
1403 }
1404
1405 static void
1406 init_attr_string(void)
1407 {
1408     attr_test_string[0] = (char) default_attr_string();
1409     adjust_attr_string(0);
1410 }
1411
1412 static int
1413 show_attr(WINDOW *win, int row, int skip, bool arrow, chtype attr, const char *name)
1414 {
1415     int ncv = get_ncv();
1416     chtype test = attr & (chtype) (~(A_ALTCHARSET | A_CHARTEXT));
1417
1418     if (arrow)
1419         MvPrintw(row, COLS_PRE_ATTRS - 3, "-->");
1420     MvPrintw(row, COLS_PRE_ATTRS, "%s mode:", name);
1421     MvPrintw(row, COL_ATTRSTRING - 1, "|");
1422     if (skip)
1423         printw("%*s", skip, " ");
1424     /*
1425      * Just for testing, write text using the alternate character set one
1426      * character at a time (to pass its rendition directly), and use the
1427      * string operation for the other attributes.
1428      */
1429     wmove(win, 0, 0);
1430     werase(win);
1431     if (attr & A_ALTCHARSET) {
1432         const char *s;
1433         chtype ch;
1434
1435         for (s = attr_test_string; *s != '\0'; ++s) {
1436             ch = UChar(*s);
1437             (void) waddch(win, ch | attr);
1438         }
1439     } else {
1440         (void) wattrset(win, AttrArg(attr, 0));
1441         (void) waddstr(win, attr_test_string);
1442         (void) wattroff(win, (int) attr);
1443     }
1444     if (skip)
1445         printw("%*s", skip, " ");
1446     MvPrintw(row, COL_ATTRSTRING + LEN_ATTRSTRING, "|");
1447     if (test != A_NORMAL) {
1448         if (!(termattrs() & test)) {
1449             printw(" (N/A)");
1450         } else {
1451             if (ncv > 0 && stdscr && (getbkgd(stdscr) & A_COLOR)) {
1452                 static const chtype table[] =
1453                 {
1454                     A_STANDOUT,
1455                     A_UNDERLINE,
1456                     A_REVERSE,
1457                     A_BLINK,
1458                     A_DIM,
1459                     A_BOLD,
1460 #ifdef A_INVIS
1461                     A_INVIS,
1462 #endif
1463 #ifdef A_ITALIC
1464                     A_ITALIC,
1465 #endif
1466                     A_PROTECT,
1467                     A_ALTCHARSET
1468                 };
1469                 unsigned n;
1470                 bool found = FALSE;
1471                 for (n = 0; n < SIZEOF(table); n++) {
1472                     if ((table[n] & attr) != 0
1473                         && ((1 << n) & ncv) != 0) {
1474                         found = TRUE;
1475                         break;
1476                     }
1477                 }
1478                 if (found)
1479                     printw(" (NCV)");
1480             }
1481             if ((termattrs() & test) != test) {
1482                 printw(" (Part)");
1483             }
1484         }
1485     }
1486     return row + 2;
1487 }
1488
1489 typedef struct {
1490     attr_t attr;
1491     NCURSES_CONST char *name;
1492 } ATTR_TBL;
1493 /* *INDENT-OFF* */
1494 static const ATTR_TBL attrs_to_test[] = {
1495     { A_STANDOUT,       "STANDOUT" },
1496     { A_REVERSE,        "REVERSE" },
1497     { A_BOLD,           "BOLD" },
1498     { A_UNDERLINE,      "UNDERLINE" },
1499     { A_DIM,            "DIM" },
1500     { A_BLINK,          "BLINK" },
1501     { A_PROTECT,        "PROTECT" },
1502 #ifdef A_INVIS
1503     { A_INVIS,          "INVISIBLE" },
1504 #endif
1505 #ifdef A_ITALIC
1506     { A_ITALIC,         "ITALIC" },
1507 #endif
1508     { A_NORMAL,         "NORMAL" },
1509 };
1510 /* *INDENT-ON* */
1511
1512 static unsigned
1513 init_attr_list(ATTR_TBL * target, attr_t attrs)
1514 {
1515     unsigned result = 0;
1516     size_t n;
1517
1518     for (n = 0; n < SIZEOF(attrs_to_test); ++n) {
1519         attr_t test = attrs_to_test[n].attr;
1520         if (test == A_NORMAL || (test & attrs) != 0) {
1521             target[result++] = attrs_to_test[n];
1522         }
1523     }
1524     return result;
1525 }
1526
1527 static bool
1528 attr_getc(int *skip,
1529           NCURSES_COLOR_T *fg,
1530           NCURSES_COLOR_T *bg,
1531           NCURSES_COLOR_T *tx,
1532           int *ac,
1533           unsigned *kc,
1534           unsigned limit)
1535 {
1536     bool result = TRUE;
1537     bool error = FALSE;
1538     WINDOW *helpwin;
1539
1540     do {
1541         int ch = Getchar();
1542
1543         error = FALSE;
1544         if (ch < 256 && isdigit(ch)) {
1545             *skip = (ch - '0');
1546         } else {
1547             switch (ch) {
1548             case CTRL('L'):
1549                 Repaint();
1550                 break;
1551             case '?':
1552                 if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
1553                     box(helpwin, 0, 0);
1554                     attr_legend(helpwin);
1555                     wGetchar(helpwin);
1556                     delwin(helpwin);
1557                 }
1558                 break;
1559             case 'a':
1560                 *ac = 0;
1561                 break;
1562             case 'A':
1563                 *ac = A_ALTCHARSET;
1564                 break;
1565             case 'v':
1566                 if (*kc == 0)
1567                     *kc = limit - 1;
1568                 else
1569                     *kc -= 1;
1570                 break;
1571             case 'V':
1572                 *kc += 1;
1573                 if (*kc >= limit)
1574                     *kc = 0;
1575                 break;
1576             case '<':
1577                 adjust_attr_string(-1);
1578                 break;
1579             case '>':
1580                 adjust_attr_string(1);
1581                 break;
1582             case case_QUIT:
1583                 result = FALSE;
1584                 break;
1585             default:
1586                 error = cycle_color_attr(ch, fg, bg, tx);
1587                 break;
1588             }
1589         }
1590     } while (error);
1591     return result;
1592 }
1593
1594 static void
1595 attr_test(void)
1596 /* test text attributes */
1597 {
1598     int n;
1599     int skip = get_xmc();
1600     NCURSES_COLOR_T fg = COLOR_BLACK;   /* color pair 0 is special */
1601     NCURSES_COLOR_T bg = COLOR_BLACK;
1602     NCURSES_COLOR_T tx = -1;
1603     int ac = 0;
1604     unsigned j, k;
1605     WINDOW *my_wins[SIZEOF(attrs_to_test)];
1606     ATTR_TBL my_list[SIZEOF(attrs_to_test)];
1607     unsigned my_size = init_attr_list(my_list, termattrs());
1608
1609     if (my_size > 1) {
1610         for (j = 0; j < my_size; ++j) {
1611             my_wins[j] = subwin(stdscr,
1612                                 1, LEN_ATTRSTRING,
1613                                 2 + (int) (2 * j), COL_ATTRSTRING);
1614             scrollok(my_wins[j], FALSE);
1615         }
1616
1617         if (skip < 0)
1618             skip = 0;
1619
1620         n = skip;               /* make it easy */
1621         k = my_size - 1;
1622         init_attr_string();
1623
1624         do {
1625             int row = 2;
1626             chtype normal = A_NORMAL | BLANK;
1627             chtype extras = (chtype) ac;
1628
1629             if (use_colors) {
1630                 NCURSES_PAIRS_T pair = 0;
1631                 if ((fg != COLOR_BLACK) || (bg != COLOR_BLACK)) {
1632                     pair = 1;
1633                     if (init_pair(pair, fg, bg) == ERR) {
1634                         beep();
1635                     } else {
1636                         normal |= (chtype) COLOR_PAIR(pair);
1637                     }
1638                 }
1639                 if (tx >= 0) {
1640                     pair = 2;
1641                     if (init_pair(pair, tx, bg) == ERR) {
1642                         beep();
1643                     } else {
1644                         extras |= (chtype) COLOR_PAIR(pair);
1645                         normal &= ~A_COLOR;
1646                     }
1647                 }
1648             }
1649             bkgd(normal);
1650             bkgdset(normal);
1651             erase();
1652
1653             box(stdscr, 0, 0);
1654             MvAddStr(0, 20, "Character attribute test display");
1655
1656             for (j = 0; j < my_size; ++j) {
1657                 bool arrow = (j == k);
1658                 row = show_attr(my_wins[j], row, n, arrow,
1659                                 normal |
1660                                 extras |
1661                                 my_list[j].attr |
1662                                 my_list[k].attr,
1663                                 my_list[j].name);
1664             }
1665
1666             MvPrintw(row, COLS_PRE_ATTRS,
1667                      "This terminal does %shave the magic-cookie glitch",
1668                      get_xmc() > -1 ? "" : "not ");
1669             MvPrintw(row + 1, COLS_PRE_ATTRS, "Enter '?' for help.");
1670             show_color_attr(fg, bg, tx);
1671             printw("  ACS (%d)", ac != 0);
1672
1673             refresh();
1674         } while (attr_getc(&n, &fg, &bg, &tx, &ac, &k, my_size));
1675
1676         bkgdset(A_NORMAL | BLANK);
1677         erase();
1678         endwin();
1679     } else {
1680         Cannot("does not support video attributes.");
1681     }
1682 }
1683
1684 #if USE_WIDEC_SUPPORT
1685 static bool use_fullwidth;
1686 static wchar_t wide_attr_test_string[MAX_ATTRSTRING + 1];
1687
1688 #define FULL_LO 0xff00
1689 #define FULL_HI 0xff5e
1690 #define HALF_LO 0x20
1691
1692 #define isFullWidth(ch)   ((int)(ch) >= FULL_LO && (int)(ch) <= FULL_HI)
1693 #define ToNormalWidth(ch) (wchar_t) (((int)(ch) - FULL_LO) + HALF_LO)
1694 #define ToFullWidth(ch)   (wchar_t) (((int)(ch) - HALF_LO) + FULL_LO)
1695
1696 /*
1697  * Returns an ASCII code in [32..126]
1698  */
1699 static wchar_t
1700 normal_wchar(int ch)
1701 {
1702     wchar_t result = (wchar_t) ch;
1703     if (isFullWidth(ch))
1704         result = ToNormalWidth(ch);
1705     return result;
1706 }
1707
1708 /*
1709  * Returns either an ASCII code in in [32..126] or full-width in
1710  * [0xff00..0xff5e], according to use_fullwidth setting.
1711  */
1712 static wchar_t
1713 target_wchar(int ch)
1714 {
1715     wchar_t result = (wchar_t) ch;
1716     if (use_fullwidth) {
1717         if (!isFullWidth(ch))
1718             result = ToFullWidth(ch);
1719     } else {
1720         if (isFullWidth(ch))
1721             result = ToNormalWidth(ch);
1722     }
1723     return result;
1724 }
1725
1726 static void
1727 wide_adjust_attr_string(int adjust)
1728 {
1729     wchar_t save = wide_attr_test_string[0];
1730     int first = ((int) normal_wchar(save)) + adjust;
1731     int j, k;
1732
1733     if (first >= ATTRSTRING_1ST) {
1734         for (j = 0, k = first; j < MAX_ATTRSTRING; ++j, ++k) {
1735             if (k > ATTRSTRING_END)
1736                 break;
1737             wide_attr_test_string[j] = target_wchar(k);
1738             if (((k + 1 - first) % 5) == 0) {
1739                 if (++j >= MAX_ATTRSTRING)
1740                     break;
1741                 wide_attr_test_string[j] = ' ';
1742             }
1743         }
1744         if ((LEN_ATTRSTRING - j) > 5) {
1745             wide_attr_test_string[0] = save;
1746             wide_adjust_attr_string(adjust - 1);
1747         } else {
1748             while (j < MAX_ATTRSTRING)
1749                 wide_attr_test_string[j++] = ' ';
1750             wide_attr_test_string[j] = '\0';
1751         }
1752     }
1753 }
1754
1755 static void
1756 wide_init_attr_string(void)
1757 {
1758     use_fullwidth = FALSE;
1759     wide_attr_test_string[0] = (wchar_t) default_attr_string();
1760     wide_adjust_attr_string(0);
1761 }
1762
1763 static void
1764 set_wide_background(NCURSES_PAIRS_T pair)
1765 {
1766     cchar_t normal;
1767     wchar_t blank[2];
1768
1769     blank[0] = ' ';
1770     blank[1] = 0;
1771     setcchar(&normal, blank, A_NORMAL, pair, 0);
1772     bkgrnd(&normal);
1773     bkgrndset(&normal);
1774 }
1775
1776 static attr_t
1777 get_wide_background(void)
1778 {
1779     attr_t result = A_NORMAL;
1780     attr_t attr;
1781     cchar_t ch;
1782     NCURSES_PAIRS_T pair;
1783     wchar_t wch[10];
1784
1785     memset(&ch, 0, sizeof(ch));
1786     if (getbkgrnd(&ch) != ERR) {
1787         if (getcchar(&ch, wch, &attr, &pair, 0) != ERR) {
1788             result = attr;
1789         }
1790     }
1791     return result;
1792 }
1793
1794 static int
1795 wide_show_attr(WINDOW *win,
1796                int row,
1797                int skip,
1798                bool arrow,
1799                chtype attr,
1800                NCURSES_PAIRS_T pair,
1801                const char *name)
1802 {
1803     int ncv = get_ncv();
1804     chtype test = attr & ~WA_ALTCHARSET;
1805
1806     if (arrow)
1807         MvPrintw(row, COLS_PRE_ATTRS - 3, "-->");
1808     MvPrintw(row, COLS_PRE_ATTRS, "%s mode:", name);
1809     MvPrintw(row, COL_ATTRSTRING - 1, "|");
1810     if (skip)
1811         printw("%*s", skip, " ");
1812
1813     /*
1814      * Just for testing, write text using the alternate character set one
1815      * character at a time (to pass its rendition directly), and use the
1816      * string operation for the other attributes.
1817      */
1818     wmove(win, 0, 0);
1819     werase(win);
1820     if (attr & WA_ALTCHARSET) {
1821         const wchar_t *s;
1822         cchar_t ch;
1823
1824         for (s = wide_attr_test_string; *s != L'\0'; ++s) {
1825             wchar_t fill[2];
1826             fill[0] = *s;
1827             fill[1] = L'\0';
1828             setcchar(&ch, fill, attr, pair, 0);
1829             (void) wadd_wch(win, &ch);
1830         }
1831     } else {
1832         attr_t old_attr = 0;
1833         NCURSES_PAIRS_T old_pair = 0;
1834
1835         (void) (wattr_get) (win, &old_attr, &old_pair, 0);
1836         (void) wattr_set(win, attr, pair, 0);
1837         (void) waddwstr(win, wide_attr_test_string);
1838         (void) wattr_set(win, old_attr, old_pair, 0);
1839     }
1840     if (skip)
1841         printw("%*s", skip, " ");
1842     MvPrintw(row, COL_ATTRSTRING + LEN_ATTRSTRING, "|");
1843     if (test != A_NORMAL) {
1844         if (!(term_attrs() & test)) {
1845             printw(" (N/A)");
1846         } else {
1847             if (ncv > 0 && (get_wide_background() & A_COLOR)) {
1848                 static const attr_t table[] =
1849                 {
1850                     WA_STANDOUT,
1851                     WA_UNDERLINE,
1852                     WA_REVERSE,
1853                     WA_BLINK,
1854                     WA_DIM,
1855                     WA_BOLD,
1856                     WA_INVIS,
1857                     WA_PROTECT,
1858                     WA_ALTCHARSET
1859                 };
1860                 unsigned n;
1861                 bool found = FALSE;
1862                 for (n = 0; n < SIZEOF(table); n++) {
1863                     if ((table[n] & attr) != 0
1864                         && ((1 << n) & ncv) != 0) {
1865                         found = TRUE;
1866                         break;
1867                     }
1868                 }
1869                 if (found)
1870                     printw(" (NCV)");
1871             }
1872             if ((term_attrs() & test) != test) {
1873                 printw(" (Part)");
1874             }
1875         }
1876     }
1877     return row + 2;
1878 }
1879
1880 static bool
1881 wide_attr_getc(int *skip,
1882                NCURSES_COLOR_T *fg, NCURSES_COLOR_T *bg,
1883                NCURSES_COLOR_T *tx, int *ac,
1884                unsigned *kc, unsigned limit)
1885 {
1886     bool result = TRUE;
1887     bool error = FALSE;
1888     WINDOW *helpwin;
1889
1890     do {
1891         int ch = Getchar();
1892
1893         error = FALSE;
1894         if (ch < 256 && isdigit(ch)) {
1895             *skip = (ch - '0');
1896         } else {
1897             switch (ch) {
1898             case CTRL('L'):
1899                 Repaint();
1900                 break;
1901             case '?':
1902                 if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
1903                     box_set(helpwin, 0, 0);
1904                     attr_legend(helpwin);
1905                     wGetchar(helpwin);
1906                     delwin(helpwin);
1907                 }
1908                 break;
1909             case 'a':
1910                 *ac = 0;
1911                 break;
1912             case 'A':
1913                 *ac = A_ALTCHARSET;
1914                 break;
1915             case 'v':
1916                 if (*kc == 0)
1917                     *kc = limit - 1;
1918                 else
1919                     *kc -= 1;
1920                 break;
1921             case 'V':
1922                 *kc += 1;
1923                 if (*kc >= limit)
1924                     *kc = 0;
1925                 break;
1926             case 'w':
1927                 use_fullwidth = FALSE;
1928                 wide_adjust_attr_string(0);
1929                 break;
1930             case 'W':
1931                 use_fullwidth = TRUE;
1932                 wide_adjust_attr_string(0);
1933                 break;
1934             case '<':
1935                 wide_adjust_attr_string(-1);
1936                 break;
1937             case '>':
1938                 wide_adjust_attr_string(1);
1939                 break;
1940             case case_QUIT:
1941                 result = FALSE;
1942                 break;
1943             default:
1944                 error = cycle_color_attr(ch, fg, bg, tx);
1945                 break;
1946             }
1947         }
1948     } while (error);
1949     return result;
1950 }
1951
1952 static void
1953 wide_attr_test(void)
1954 /* test text attributes using wide-character calls */
1955 {
1956     int n;
1957     int skip = get_xmc();
1958     NCURSES_COLOR_T fg = COLOR_BLACK;   /* color pair 0 is special */
1959     NCURSES_COLOR_T bg = COLOR_BLACK;
1960     NCURSES_COLOR_T tx = -1;
1961     int ac = 0;
1962     unsigned j, k;
1963     ATTR_TBL my_list[SIZEOF(attrs_to_test)];
1964     WINDOW *my_wins[SIZEOF(attrs_to_test)];
1965     unsigned my_size = init_attr_list(my_list, term_attrs());
1966
1967     if (my_size > 1) {
1968         for (j = 0; j < my_size; ++j) {
1969             my_wins[j] = subwin(stdscr,
1970                                 1, LEN_ATTRSTRING,
1971                                 2 + (int) (2 * j), COL_ATTRSTRING);
1972             scrollok(my_wins[j], FALSE);
1973         }
1974
1975         if (skip < 0)
1976             skip = 0;
1977
1978         n = skip;               /* make it easy */
1979         k = my_size - 1;
1980         wide_init_attr_string();
1981
1982         do {
1983             int row = 2;
1984             NCURSES_PAIRS_T pair = 0;
1985             NCURSES_PAIRS_T extras = 0;
1986
1987             if (use_colors) {
1988                 pair = (NCURSES_PAIRS_T) (fg != COLOR_BLACK || bg != COLOR_BLACK);
1989                 if (pair != 0) {
1990                     pair = 1;
1991                     if (init_pair(pair, fg, bg) == ERR) {
1992                         beep();
1993                     }
1994                 }
1995                 extras = pair;
1996                 if (tx >= 0) {
1997                     extras = 2;
1998                     if (init_pair(extras, tx, bg) == ERR) {
1999                         beep();
2000                     }
2001                 }
2002             }
2003             set_wide_background(pair);
2004             erase();
2005
2006             box_set(stdscr, 0, 0);
2007             MvAddStr(0, 20, "Character attribute test display");
2008
2009             for (j = 0; j < my_size; ++j) {
2010                 row = wide_show_attr(my_wins[j], row, n, (j == k),
2011                                      ((attr_t) ac |
2012                                       my_list[j].attr |
2013                                       my_list[k].attr),
2014                                      extras,
2015                                      my_list[j].name);
2016             }
2017
2018             MvPrintw(row, COLS_PRE_ATTRS,
2019                      "This terminal does %shave the magic-cookie glitch",
2020                      get_xmc() > -1 ? "" : "not ");
2021             MvPrintw(row + 1, COLS_PRE_ATTRS, "Enter '?' for help.");
2022             show_color_attr(fg, bg, tx);
2023             printw("  ACS (%d)", ac != 0);
2024
2025             refresh();
2026         } while (wide_attr_getc(&n, &fg, &bg, &tx, &ac, &k, my_size));
2027
2028         set_wide_background(0);
2029         erase();
2030         endwin();
2031     } else {
2032         Cannot("does not support extended video attributes.");
2033     }
2034 }
2035 #endif
2036
2037 /****************************************************************************
2038  *
2039  * Color support tests
2040  *
2041  ****************************************************************************/
2042
2043 static NCURSES_CONST char *the_color_names[] =
2044 {
2045     "black",
2046     "red",
2047     "green",
2048     "yellow",
2049     "blue",
2050     "magenta",
2051     "cyan",
2052     "white",
2053     "BLACK",
2054     "RED",
2055     "GREEN",
2056     "YELLOW",
2057     "BLUE",
2058     "MAGENTA",
2059     "CYAN",
2060     "WHITE"
2061 };
2062
2063 static void
2064 show_color_name(int y, int x, int color, bool wide)
2065 {
2066     if (move(y, x) != ERR) {
2067         char temp[80];
2068         int width = 8;
2069
2070         if (wide) {
2071             sprintf(temp, "%02d", color);
2072             width = 4;
2073         } else if (color >= 8) {
2074             sprintf(temp, "[%02d]", color);
2075         } else if (color < 0) {
2076             strcpy(temp, "default");
2077         } else {
2078             sprintf(temp, "%.*s", 16, the_color_names[color]);
2079         }
2080         printw("%-*.*s", width, width, temp);
2081     }
2082 }
2083
2084 static void
2085 color_legend(WINDOW *helpwin, bool wide)
2086 {
2087     int row = 1;
2088     int col = 1;
2089
2090     MvWPrintw(helpwin, row++, col,
2091               "ESC to exit.");
2092     ++row;
2093     MvWPrintw(helpwin, row++, col,
2094               "Use up/down arrow to scroll through the display if it is");
2095     MvWPrintw(helpwin, row++, col,
2096               "longer than one screen. Control/N and Control/P can be used");
2097     MvWPrintw(helpwin, row++, col,
2098               "in place of up/down arrow.  Use pageup/pagedown to scroll a");
2099     MvWPrintw(helpwin, row++, col,
2100               "full screen; control/B and control/F can be used here.");
2101     ++row;
2102     MvWPrintw(helpwin, row++, col,
2103               "Toggles:");
2104     MvWPrintw(helpwin, row++, col,
2105               "  a/A     toggle altcharset off/on");
2106     MvWPrintw(helpwin, row++, col,
2107               "  b/B     toggle bold off/on");
2108     if (has_colors()) {
2109         MvWPrintw(helpwin, row++, col,
2110                   "  c/C     cycle used-colors through 8,16,...,COLORS");
2111     }
2112     MvWPrintw(helpwin, row++, col,
2113               "  n/N     toggle text/number on/off");
2114     MvWPrintw(helpwin, row++, col,
2115               "  r/R     toggle reverse on/off");
2116     MvWPrintw(helpwin, row++, col,
2117               "  w/W     toggle width between 8/16 colors");
2118 #if USE_WIDEC_SUPPORT
2119     if (wide) {
2120         MvWPrintw(helpwin, row++, col,
2121                   "Wide characters:");
2122         MvWPrintw(helpwin, row, col,
2123                   "  x/X     toggle text between ASCII and wide-character");
2124     }
2125 #else
2126     (void) wide;
2127 #endif
2128 }
2129
2130 #define set_color_test(name, value) if (name != value) { name = value; base_row = 0; }
2131
2132 static int
2133 color_cycle(int current, int step)
2134 {
2135     int result = current;
2136     if (step < 0) {
2137         if (current <= 8) {
2138             result = COLORS;
2139         } else {
2140             result = 8;
2141             if ((result * 2) > COLORS) {
2142                 result = COLORS;
2143             } else {
2144                 while ((result * 2) < current) {
2145                     result *= 2;
2146                 }
2147             }
2148         }
2149     } else {
2150         if (current >= COLORS) {
2151             result = 8;
2152         } else {
2153             result *= 2;
2154         }
2155         if (result > COLORS)
2156             result = COLORS;
2157     }
2158     return result;
2159 }
2160
2161 /* generate a color test pattern */
2162 static void
2163 color_test(void)
2164 {
2165     NCURSES_PAIRS_T i;
2166     int top = 0, width;
2167     int base_row = 0;
2168     int grid_top = top + 3;
2169     int page_size = (LINES - grid_top);
2170     int pairs_max;
2171     int colors_max = COLORS;
2172     int row_limit;
2173     int per_row;
2174     char numbered[80];
2175     const char *hello;
2176     bool done = FALSE;
2177     bool opt_acsc = FALSE;
2178     bool opt_bold = FALSE;
2179     bool opt_revs = FALSE;
2180     bool opt_nums = FALSE;
2181     bool opt_wide = FALSE;
2182     WINDOW *helpwin;
2183
2184     while (!done) {
2185         int shown = 0;
2186
2187         pairs_max = PAIR_NUMBER(A_COLOR) + 1;
2188         if (colors_max * colors_max <= COLOR_PAIRS) {
2189             int limit = (colors_max - min_colors) * (colors_max - min_colors);
2190             if (pairs_max > limit)
2191                 pairs_max = limit;
2192         } else {
2193             if (pairs_max > COLOR_PAIRS)
2194                 pairs_max = COLOR_PAIRS;
2195         }
2196
2197         /* this assumes an 80-column line */
2198         if (opt_wide) {
2199             width = 4;
2200             hello = "Test";
2201             per_row = (colors_max > 8) ? 16 : 8;
2202         } else {
2203             width = 8;
2204             hello = "Hello";
2205             per_row = 8;
2206         }
2207         per_row -= min_colors;
2208
2209         row_limit = (pairs_max + per_row - 1) / per_row;
2210
2211         move(0, 0);
2212         (void) printw("There are %d color pairs and %d colors",
2213                       pairs_max, COLORS);
2214         if (colors_max != COLORS)
2215             (void) printw(" (using %d colors)", colors_max);
2216         if (min_colors)
2217             (void) addstr(" besides 'default'");
2218
2219         clrtobot();
2220         MvPrintw(top + 1, 0,
2221                  "%dx%d matrix of foreground/background colors, bold *%s*\n",
2222                  row_limit,
2223                  per_row,
2224                  opt_bold ? "on" : "off");
2225
2226         /* show color names/numbers across the top */
2227         for (i = 0; i < per_row; i++)
2228             show_color_name(top + 2, (i + 1) * width, i + min_colors, opt_wide);
2229
2230         /* show a grid of colors, with color names/ numbers on the left */
2231         for (i = (NCURSES_PAIRS_T) (base_row * per_row); i < pairs_max; i++) {
2232             int row = grid_top + (i / per_row) - base_row;
2233             int col = (i % per_row + 1) * width;
2234             NCURSES_PAIRS_T pair = i;
2235
2236 #define InxToFG(i) (NCURSES_COLOR_T) ((i % (colors_max - min_colors)) + min_colors)
2237 #define InxToBG(i) (NCURSES_COLOR_T) ((i / (colors_max - min_colors)) + min_colors)
2238             if (row >= 0 && move(row, col) != ERR) {
2239                 NCURSES_COLOR_T fg = InxToFG(i);
2240                 NCURSES_COLOR_T bg = InxToBG(i);
2241
2242                 init_pair(pair, fg, bg);
2243                 attron(COLOR_PAIR(pair));
2244                 if (opt_acsc)
2245                     attron(A_ALTCHARSET);
2246                 if (opt_bold)
2247                     attron(A_BOLD);
2248                 if (opt_revs)
2249                     attron(A_REVERSE);
2250
2251                 if (opt_nums) {
2252                     sprintf(numbered, "{%02X}", (int) i);
2253                     hello = numbered;
2254                 }
2255                 printw("%-*.*s", width, width, hello);
2256                 (void) attrset(A_NORMAL);
2257
2258                 if ((i % per_row) == 0 && InxToFG(i) == min_colors) {
2259                     show_color_name(row, 0, InxToBG(i), opt_wide);
2260                 }
2261                 ++shown;
2262             } else if (shown) {
2263                 break;
2264             }
2265         }
2266
2267         switch (wGetchar(stdscr)) {
2268         case 'a':
2269             opt_acsc = FALSE;
2270             break;
2271         case 'A':
2272             opt_acsc = TRUE;
2273             break;
2274         case 'b':
2275             opt_bold = FALSE;
2276             break;
2277         case 'B':
2278             opt_bold = TRUE;
2279             break;
2280         case 'c':
2281             colors_max = color_cycle(colors_max, -1);
2282             break;
2283         case 'C':
2284             colors_max = color_cycle(colors_max, 1);
2285             break;
2286         case 'n':
2287             opt_nums = FALSE;
2288             break;
2289         case 'N':
2290             opt_nums = TRUE;
2291             break;
2292         case 'r':
2293             opt_revs = FALSE;
2294             break;
2295         case 'R':
2296             opt_revs = TRUE;
2297             break;
2298         case case_QUIT:
2299             done = TRUE;
2300             continue;
2301         case 'w':
2302             set_color_test(opt_wide, FALSE);
2303             break;
2304         case 'W':
2305             set_color_test(opt_wide, TRUE);
2306             break;
2307         case CTRL('p'):
2308         case KEY_UP:
2309             if (base_row <= 0) {
2310                 beep();
2311             } else {
2312                 base_row -= 1;
2313             }
2314             break;
2315         case CTRL('n'):
2316         case KEY_DOWN:
2317             if (base_row + page_size >= row_limit) {
2318                 beep();
2319             } else {
2320                 base_row += 1;
2321             }
2322             break;
2323         case CTRL('b'):
2324         case KEY_PREVIOUS:
2325         case KEY_PPAGE:
2326             if (base_row <= 0) {
2327                 beep();
2328             } else {
2329                 base_row -= (page_size - 1);
2330                 if (base_row < 0)
2331                     base_row = 0;
2332             }
2333             break;
2334         case CTRL('f'):
2335         case KEY_NEXT:
2336         case KEY_NPAGE:
2337             if (base_row + page_size >= row_limit) {
2338                 beep();
2339             } else {
2340                 base_row += page_size - 1;
2341                 if (base_row + page_size >= row_limit) {
2342                     base_row = row_limit - page_size - 1;
2343                 }
2344             }
2345             break;
2346         case '?':
2347             if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
2348                 box(helpwin, 0, 0);
2349                 color_legend(helpwin, FALSE);
2350                 wGetchar(helpwin);
2351                 delwin(helpwin);
2352             }
2353             break;
2354         default:
2355             beep();
2356             continue;
2357         }
2358     }
2359
2360     erase();
2361     endwin();
2362 }
2363
2364 #if USE_WIDEC_SUPPORT
2365 /* generate a color test pattern */
2366 static void
2367 wide_color_test(void)
2368 {
2369     int i;
2370     int top = 0, width;
2371     int base_row = 0;
2372     int grid_top = top + 3;
2373     int page_size = (LINES - grid_top);
2374     int pairs_max = (unsigned short) (-1);
2375     int colors_max = COLORS;
2376     int row_limit;
2377     int per_row;
2378     char numbered[80];
2379     const char *hello;
2380     bool done = FALSE;
2381     bool opt_acsc = FALSE;
2382     bool opt_bold = FALSE;
2383     bool opt_revs = FALSE;
2384     bool opt_wide = FALSE;
2385     bool opt_nums = FALSE;
2386     bool opt_xchr = FALSE;
2387     wchar_t buffer[80];
2388     WINDOW *helpwin;
2389
2390     while (!done) {
2391         int shown = 0;
2392
2393         pairs_max = (unsigned short) (-1);
2394         if (colors_max * colors_max <= COLOR_PAIRS) {
2395             int limit = (colors_max - min_colors) * (colors_max - min_colors);
2396             if (pairs_max > limit)
2397                 pairs_max = limit;
2398         } else {
2399             if (pairs_max > COLOR_PAIRS)
2400                 pairs_max = COLOR_PAIRS;
2401         }
2402
2403         /* this assumes an 80-column line */
2404         if (opt_wide) {
2405             width = 4;
2406             hello = "Test";
2407             per_row = (colors_max > 8) ? 16 : 8;
2408         } else {
2409             width = 8;
2410             hello = "Hello";
2411             per_row = 8;
2412         }
2413         per_row -= min_colors;
2414
2415         if (opt_xchr) {
2416             make_fullwidth_text(buffer, hello);
2417             width *= 2;
2418             per_row /= 2;
2419         } else {
2420             make_narrow_text(buffer, hello);
2421         }
2422
2423         row_limit = (pairs_max + per_row - 1) / per_row;
2424
2425         move(0, 0);
2426         (void) printw("There are %d color pairs and %d colors",
2427                       pairs_max, COLORS);
2428         if (colors_max != COLORS)
2429             (void) printw(" (using %d colors)", colors_max);
2430         if (min_colors)
2431             (void) addstr(" besides 'default'");
2432
2433         clrtobot();
2434         MvPrintw(top + 1, 0,
2435                  "%dx%d matrix of foreground/background colors, bold *%s*\n",
2436                  row_limit,
2437                  per_row,
2438                  opt_bold ? "on" : "off");
2439
2440         /* show color names/numbers across the top */
2441         for (i = 0; i < per_row; i++)
2442             show_color_name(top + 2, (i + 1) * width, i + min_colors, opt_wide);
2443
2444         /* show a grid of colors, with color names/ numbers on the left */
2445         for (i = (base_row * per_row); i < pairs_max; i++) {
2446             int row = grid_top + (i / per_row) - base_row;
2447             int col = (i % per_row + 1) * width;
2448             NCURSES_PAIRS_T pair = (NCURSES_PAIRS_T) i;
2449
2450             if (row >= 0 && move(row, col) != ERR) {
2451                 init_pair(pair, InxToFG(i), InxToBG(i));
2452                 (void) color_set(pair, NULL);
2453                 if (opt_acsc)
2454                     attr_on(A_ALTCHARSET, NULL);
2455                 if (opt_bold)
2456                     attr_on(A_BOLD, NULL);
2457                 if (opt_revs)
2458                     attr_on(A_REVERSE, NULL);
2459
2460                 if (opt_nums) {
2461                     sprintf(numbered, "{%02X}", i);
2462                     if (opt_xchr) {
2463                         make_fullwidth_text(buffer, numbered);
2464                     } else {
2465                         make_narrow_text(buffer, numbered);
2466                     }
2467                 }
2468                 addnwstr(buffer, width);
2469                 (void) attr_set(A_NORMAL, 0, NULL);
2470
2471                 if ((i % per_row) == 0 && InxToFG(i) == min_colors) {
2472                     show_color_name(row, 0, InxToBG(i), opt_wide);
2473                 }
2474                 ++shown;
2475             } else if (shown) {
2476                 break;
2477             }
2478         }
2479
2480         switch (wGetchar(stdscr)) {
2481         case 'a':
2482             opt_acsc = FALSE;
2483             break;
2484         case 'A':
2485             opt_acsc = TRUE;
2486             break;
2487         case 'b':
2488             opt_bold = FALSE;
2489             break;
2490         case 'B':
2491             opt_bold = TRUE;
2492             break;
2493         case 'c':
2494             colors_max = color_cycle(colors_max, -1);
2495             break;
2496         case 'C':
2497             colors_max = color_cycle(colors_max, 1);
2498             break;
2499         case 'n':
2500             opt_nums = FALSE;
2501             break;
2502         case 'N':
2503             opt_nums = TRUE;
2504             break;
2505         case 'r':
2506             opt_revs = FALSE;
2507             break;
2508         case 'R':
2509             opt_revs = TRUE;
2510             break;
2511         case case_QUIT:
2512             done = TRUE;
2513             continue;
2514         case 'w':
2515             set_color_test(opt_wide, FALSE);
2516             break;
2517         case 'W':
2518             set_color_test(opt_wide, TRUE);
2519             break;
2520         case 'x':
2521             opt_xchr = FALSE;
2522             break;
2523         case 'X':
2524             opt_xchr = TRUE;
2525             break;
2526         case CTRL('p'):
2527         case KEY_UP:
2528             if (base_row <= 0) {
2529                 beep();
2530             } else {
2531                 base_row -= 1;
2532             }
2533             break;
2534         case CTRL('n'):
2535         case KEY_DOWN:
2536             if (base_row + page_size >= row_limit) {
2537                 beep();
2538             } else {
2539                 base_row += 1;
2540             }
2541             break;
2542         case CTRL('b'):
2543         case KEY_PREVIOUS:
2544         case KEY_PPAGE:
2545             if (base_row <= 0) {
2546                 beep();
2547             } else {
2548                 base_row -= (page_size - 1);
2549                 if (base_row < 0)
2550                     base_row = 0;
2551             }
2552             break;
2553         case CTRL('f'):
2554         case KEY_NEXT:
2555         case KEY_NPAGE:
2556             if (base_row + page_size >= row_limit) {
2557                 beep();
2558             } else {
2559                 base_row += page_size - 1;
2560                 if (base_row + page_size >= row_limit) {
2561                     base_row = row_limit - page_size - 1;
2562                 }
2563             }
2564             break;
2565         case '?':
2566             if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
2567                 box(helpwin, 0, 0);
2568                 color_legend(helpwin, TRUE);
2569                 wGetchar(helpwin);
2570                 delwin(helpwin);
2571             }
2572             break;
2573         default:
2574             beep();
2575             continue;
2576         }
2577     }
2578
2579     erase();
2580     endwin();
2581 }
2582 #endif /* USE_WIDEC_SUPPORT */
2583
2584 static void
2585 change_color(NCURSES_PAIRS_T current, int field, int value, int usebase)
2586 {
2587     NCURSES_COLOR_T red, green, blue;
2588
2589     color_content(current, &red, &green, &blue);
2590
2591     switch (field) {
2592     case 0:
2593         red = (NCURSES_COLOR_T) (usebase ? (red + value) : value);
2594         break;
2595     case 1:
2596         green = (NCURSES_COLOR_T) (usebase ? (green + value) : value);
2597         break;
2598     case 2:
2599         blue = (NCURSES_COLOR_T) (usebase ? (blue + value) : value);
2600         break;
2601     }
2602
2603     if (init_color(current, red, green, blue) == ERR)
2604         beep();
2605 }
2606
2607 static void
2608 init_all_colors(void)
2609 {
2610     NCURSES_PAIRS_T c;
2611
2612     for (c = 0; c < COLORS; ++c)
2613         init_color(c,
2614                    all_colors[c].red,
2615                    all_colors[c].green,
2616                    all_colors[c].blue);
2617 }
2618
2619 #define scaled_rgb(n) ((255 * (n)) / 1000)
2620
2621 static void
2622 color_edit(void)
2623 /* display the color test pattern, without trying to edit colors */
2624 {
2625     int i;
2626     int current = 0;
2627     int this_c = 0, value = 0, field = 0;
2628     int last_c;
2629     int top_color = 0;
2630     int page_size = (LINES - 6);
2631
2632     init_all_colors();
2633     refresh();
2634
2635     for (i = 0; i < max_colors; i++)
2636         init_pair((NCURSES_PAIRS_T) i,
2637                   (NCURSES_COLOR_T) COLOR_WHITE,
2638                   (NCURSES_COLOR_T) i);
2639
2640     MvPrintw(LINES - 2, 0, "Number: %d", value);
2641
2642     do {
2643         NCURSES_COLOR_T red, green, blue;
2644
2645         attron(A_BOLD);
2646         MvAddStr(0, 20, "Color RGB Value Editing");
2647         attroff(A_BOLD);
2648
2649         for (i = (NCURSES_COLOR_T) top_color;
2650              (i - top_color < page_size)
2651              && (i < max_colors); i++) {
2652             char numeric[80];
2653
2654             sprintf(numeric, "[%d]", i);
2655             MvPrintw(2 + i - top_color, 0, "%c %-8s:",
2656                      (i == current ? '>' : ' '),
2657                      (i < (int) SIZEOF(the_color_names)
2658                       ? the_color_names[i] : numeric));
2659             (void) attrset(AttrArg(COLOR_PAIR(i), 0));
2660             addstr("        ");
2661             (void) attrset(A_NORMAL);
2662
2663             color_content((NCURSES_PAIRS_T) i, &red, &green, &blue);
2664             addstr("   R = ");
2665             if (current == i && field == 0)
2666                 attron(A_STANDOUT);
2667             printw("%04d", (int) red);
2668             if (current == i && field == 0)
2669                 (void) attrset(A_NORMAL);
2670             addstr(", G = ");
2671             if (current == i && field == 1)
2672                 attron(A_STANDOUT);
2673             printw("%04d", (int) green);
2674             if (current == i && field == 1)
2675                 (void) attrset(A_NORMAL);
2676             addstr(", B = ");
2677             if (current == i && field == 2)
2678                 attron(A_STANDOUT);
2679             printw("%04d", (int) blue);
2680             if (current == i && field == 2)
2681                 (void) attrset(A_NORMAL);
2682             (void) attrset(A_NORMAL);
2683             printw(" ( %3d %3d %3d )",
2684                    (int) scaled_rgb(red),
2685                    (int) scaled_rgb(green),
2686                    (int) scaled_rgb(blue));
2687         }
2688
2689         MvAddStr(LINES - 3, 0,
2690                  "Use up/down to select a color, left/right to change fields.");
2691         MvAddStr(LINES - 2, 0,
2692                  "Modify field by typing nnn=, nnn-, or nnn+.  ? for help.");
2693
2694         move(2 + current - top_color, 0);
2695
2696         last_c = this_c;
2697         this_c = Getchar();
2698         if (this_c < 256 && isdigit(this_c) && !isdigit(last_c))
2699             value = 0;
2700
2701         switch (this_c) {
2702         case CTRL('b'):
2703         case KEY_PPAGE:
2704             if (current > 0)
2705                 current -= (page_size - 1);
2706             else
2707                 beep();
2708             break;
2709
2710         case CTRL('f'):
2711         case KEY_NPAGE:
2712             if (current < (max_colors - 1))
2713                 current += (page_size - 1);
2714             else
2715                 beep();
2716             break;
2717
2718         case CTRL('p'):
2719         case KEY_UP:
2720             current = (current == 0 ? (max_colors - 1) : current - 1);
2721             break;
2722
2723         case CTRL('n'):
2724         case KEY_DOWN:
2725             current = (current == (max_colors - 1) ? 0 : current + 1);
2726             break;
2727
2728         case KEY_RIGHT:
2729             field = (field == 2 ? 0 : field + 1);
2730             break;
2731
2732         case KEY_LEFT:
2733             field = (field == 0 ? 2 : field - 1);
2734             break;
2735
2736         case '0':
2737         case '1':
2738         case '2':
2739         case '3':
2740         case '4':
2741         case '5':
2742         case '6':
2743         case '7':
2744         case '8':
2745         case '9':
2746             value = value * 10 + (this_c - '0');
2747             break;
2748
2749         case '+':
2750             change_color((NCURSES_PAIRS_T) current, field, value, 1);
2751             break;
2752
2753         case '-':
2754             change_color((NCURSES_PAIRS_T) current, field, -value, 1);
2755             break;
2756
2757         case '=':
2758             change_color((NCURSES_PAIRS_T) current, field, value, 0);
2759             break;
2760
2761         case '?':
2762             erase();
2763             P("                      RGB Value Editing Help");
2764             P("");
2765             P("You are in the RGB value editor.  Use the arrow keys to select one of");
2766             P("the fields in one of the RGB triples of the current colors; the one");
2767             P("currently selected will be reverse-video highlighted.");
2768             P("");
2769             P("To change a field, enter the digits of the new value; they are echoed");
2770             P("as entered.  Finish by typing `='.  The change will take effect instantly.");
2771             P("To increment or decrement a value, use the same procedure, but finish");
2772             P("with a `+' or `-'.");
2773             P("");
2774             P("Press 'm' to invoke the top-level menu with the current color settings.");
2775             P("To quit, do ESC");
2776
2777             Pause();
2778             erase();
2779             break;
2780
2781         case 'm':
2782             endwin();
2783             main_menu(FALSE);
2784             for (i = 0; i < max_colors; i++)
2785                 init_pair((NCURSES_PAIRS_T) i,
2786                           (NCURSES_COLOR_T) COLOR_WHITE,
2787                           (NCURSES_COLOR_T) i);
2788             refresh();
2789             break;
2790
2791         case case_QUIT:
2792             break;
2793
2794         default:
2795             beep();
2796             break;
2797         }
2798
2799         if (current < 0)
2800             current = 0;
2801         if (current >= max_colors)
2802             current = max_colors - 1;
2803         if (current < top_color)
2804             top_color = current;
2805         if (current - top_color >= page_size)
2806             top_color = current - (page_size - 1);
2807
2808         MvPrintw(LINES - 1, 0, "Number: %d", value);
2809         clrtoeol();
2810     } while
2811         (!isQuit(this_c));
2812
2813     erase();
2814
2815     /*
2816      * ncurses does not reset each color individually when calling endwin().
2817      */
2818     init_all_colors();
2819
2820     endwin();
2821 }
2822
2823 /****************************************************************************
2824  *
2825  * Alternate character-set stuff
2826  *
2827  ****************************************************************************/
2828 static bool
2829 cycle_attr(int ch, unsigned *at_code, chtype *attr, ATTR_TBL * list, unsigned limit)
2830 {
2831     bool result = TRUE;
2832
2833     switch (ch) {
2834     case 'v':
2835         if ((*at_code += 1) >= limit)
2836             *at_code = 0;
2837         break;
2838     case 'V':
2839         if (*at_code == 0)
2840             *at_code = limit - 1;
2841         else
2842             *at_code -= 1;
2843         break;
2844     default:
2845         result = FALSE;
2846         break;
2847     }
2848     if (result)
2849         *attr = list[*at_code].attr;
2850     return result;
2851 }
2852
2853 static bool
2854 cycle_colors(int ch, int *fg, int *bg, NCURSES_PAIRS_T *pair)
2855 {
2856     bool result = FALSE;
2857
2858     if (use_colors) {
2859         result = TRUE;
2860         switch (ch) {
2861         case 'F':
2862             if ((*fg -= 1) < 0)
2863                 *fg = COLORS - 1;
2864             break;
2865         case 'f':
2866             if ((*fg += 1) >= COLORS)
2867                 *fg = 0;
2868             break;
2869         case 'B':
2870             if ((*bg -= 1) < 0)
2871                 *bg = COLORS - 1;
2872             break;
2873         case 'b':
2874             if ((*bg += 1) >= COLORS)
2875                 *bg = 0;
2876             break;
2877         default:
2878             result = FALSE;
2879             break;
2880         }
2881         if (result) {
2882             *pair = (NCURSES_PAIRS_T) (*fg != COLOR_BLACK || *bg != COLOR_BLACK);
2883             if (*pair != 0) {
2884                 *pair = 1;
2885                 if (init_pair(*pair,
2886                               (NCURSES_COLOR_T) *fg,
2887                               (NCURSES_COLOR_T) *bg) == ERR) {
2888                     result = FALSE;
2889                 }
2890             }
2891         }
2892     }
2893     return result;
2894 }
2895
2896 /****************************************************************************
2897  *
2898  * Soft-key label test
2899  *
2900  ****************************************************************************/
2901
2902 #if USE_SOFTKEYS
2903
2904 #define SLK_HELP 17
2905 #define SLK_WORK (SLK_HELP + 3)
2906
2907 static void
2908 slk_help(void)
2909 {
2910     static const char *table[] =
2911     {
2912         "Available commands are:"
2913         ,""
2914         ,"^L         -- repaint this message and activate soft keys"
2915         ,"a/d        -- activate/disable soft keys"
2916         ,"c          -- set centered format for labels"
2917         ,"l          -- set left-justified format for labels"
2918         ,"r          -- set right-justified format for labels"
2919         ,"[12345678] -- set label; labels are numbered 1 through 8"
2920         ,"e          -- erase stdscr (should not erase labels)"
2921         ,"s          -- test scrolling of shortened screen"
2922         ,"v/V        -- cycle through video attributes"
2923 #if HAVE_SLK_COLOR
2924         ,"F/f/B/b    -- cycle through foreground/background colors"
2925 #endif
2926         ,"ESC        -- return to main menu"
2927         ,""
2928         ,"Note: if activating the soft keys causes your terminal to scroll up"
2929         ,"one line, your terminal auto-scrolls when anything is written to the"
2930         ,"last screen position.  The ncurses code does not yet handle this"
2931         ,"gracefully."
2932     };
2933     unsigned j;
2934
2935     move(2, 0);
2936     for (j = 0; j < SIZEOF(table); ++j) {
2937         P(table[j]);
2938     }
2939     refresh();
2940 }
2941
2942 #if HAVE_SLK_COLOR
2943 static void
2944 call_slk_color(int fg, int bg)
2945 {
2946     init_pair(1, (NCURSES_COLOR_T) bg, (NCURSES_COLOR_T) fg);
2947     slk_color(1);
2948     MvPrintw(SLK_WORK, 0, "Colors %d/%d\n", fg, bg);
2949     clrtoeol();
2950     slk_touch();
2951     slk_noutrefresh();
2952     refresh();
2953 }
2954 #endif
2955
2956 static void
2957 slk_test(void)
2958 /* exercise the soft keys */
2959 {
2960     int c, fmt = 1;
2961     char buf[9];
2962     char *s;
2963     chtype attr = A_NORMAL;
2964     unsigned at_code = 0;
2965 #if HAVE_SLK_COLOR
2966     int fg = COLOR_BLACK;
2967     int bg = COLOR_WHITE;
2968     NCURSES_PAIRS_T pair = 0;
2969 #endif
2970     ATTR_TBL my_list[SIZEOF(attrs_to_test)];
2971     unsigned my_size = init_attr_list(my_list, termattrs());
2972
2973     c = CTRL('l');
2974 #if HAVE_SLK_COLOR
2975     if (use_colors) {
2976         call_slk_color(fg, bg);
2977     }
2978 #endif
2979
2980     do {
2981         move(0, 0);
2982         switch (c) {
2983         case CTRL('l'):
2984             erase();
2985             attron(A_BOLD);
2986             MvAddStr(0, 20, "Soft Key Exerciser");
2987             attroff(A_BOLD);
2988
2989             slk_help();
2990             /* fall through */
2991
2992         case 'a':
2993             slk_restore();
2994             break;
2995
2996         case 'e':
2997             wclear(stdscr);
2998             break;
2999
3000         case 's':
3001             MvPrintw(SLK_WORK, 0, "Press Q to stop the scrolling-test: ");
3002             while ((c = Getchar()) != 'Q' && (c != ERR))
3003                 addch((chtype) c);
3004             break;
3005
3006         case 'd':
3007             slk_clear();
3008             break;
3009
3010         case 'l':
3011             fmt = 0;
3012             break;
3013
3014         case 'c':
3015             fmt = 1;
3016             break;
3017
3018         case 'r':
3019             fmt = 2;
3020             break;
3021
3022         case '1':
3023         case '2':
3024         case '3':
3025         case '4':
3026         case '5':
3027         case '6':
3028         case '7':
3029         case '8':
3030             MvAddStr(SLK_WORK, 0, "Please enter the label value: ");
3031             strcpy(buf, "");
3032             if ((s = slk_label(c - '0')) != 0) {
3033                 strncpy(buf, s, (size_t) 8);
3034             }
3035             wGetstring(stdscr, buf, 8);
3036             slk_set((c - '0'), buf, fmt);
3037             slk_refresh();
3038             move(SLK_WORK, 0);
3039             clrtobot();
3040             break;
3041
3042         case case_QUIT:
3043             goto done;
3044
3045 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
3046         case KEY_RESIZE:
3047             wnoutrefresh(stdscr);
3048             break;
3049 #endif
3050
3051         default:
3052             if (cycle_attr(c, &at_code, &attr, my_list, my_size)) {
3053                 slk_attrset(attr);
3054                 slk_touch();
3055                 slk_noutrefresh();
3056                 break;
3057             }
3058 #if HAVE_SLK_COLOR
3059             if (cycle_colors(c, &fg, &bg, &pair)) {
3060                 if (use_colors) {
3061                     call_slk_color(fg, bg);
3062                 } else {
3063                     beep();
3064                 }
3065                 break;
3066             }
3067 #endif
3068             beep();
3069             break;
3070         }
3071     } while (!isQuit(c = Getchar()));
3072
3073   done:
3074     slk_clear();
3075     erase();
3076     endwin();
3077 }
3078
3079 #if USE_WIDEC_SUPPORT
3080 #define SLKLEN 8
3081 static void
3082 wide_slk_test(void)
3083 /* exercise the soft keys */
3084 {
3085     int c, fmt = 1;
3086     wchar_t buf[SLKLEN + 1];
3087     char *s;
3088     chtype attr = A_NORMAL;
3089     unsigned at_code = 0;
3090     int fg = COLOR_BLACK;
3091     int bg = COLOR_WHITE;
3092     NCURSES_PAIRS_T pair = 0;
3093     ATTR_TBL my_list[SIZEOF(attrs_to_test)];
3094     unsigned my_size = init_attr_list(my_list, term_attrs());
3095
3096     c = CTRL('l');
3097     if (use_colors) {
3098         call_slk_color(fg, bg);
3099     }
3100     do {
3101         move(0, 0);
3102         switch (c) {
3103         case CTRL('l'):
3104             erase();
3105             attr_on(WA_BOLD, NULL);
3106             MvAddStr(0, 20, "Soft Key Exerciser");
3107             attr_off(WA_BOLD, NULL);
3108
3109             slk_help();
3110             /* fall through */
3111
3112         case 'a':
3113             slk_restore();
3114             break;
3115
3116         case 'e':
3117             wclear(stdscr);
3118             break;
3119
3120         case 's':
3121             MvPrintw(SLK_WORK, 0, "Press Q to stop the scrolling-test: ");
3122             while ((c = Getchar()) != 'Q' && (c != ERR))
3123                 addch((chtype) c);
3124             break;
3125
3126         case 'd':
3127             slk_clear();
3128             break;
3129
3130         case 'l':
3131             fmt = 0;
3132             break;
3133
3134         case 'c':
3135             fmt = 1;
3136             break;
3137
3138         case 'r':
3139             fmt = 2;
3140             break;
3141
3142         case '1':
3143         case '2':
3144         case '3':
3145         case '4':
3146         case '5':
3147         case '6':
3148         case '7':
3149         case '8':
3150             MvAddStr(SLK_WORK, 0, "Please enter the label value: ");
3151             *buf = 0;
3152             if ((s = slk_label(c - '0')) != 0) {
3153                 char *temp = strdup(s);
3154                 size_t used = strlen(temp);
3155                 size_t want = SLKLEN;
3156                 size_t test;
3157 #ifndef state_unused
3158                 mbstate_t state;
3159 #endif
3160
3161                 buf[0] = L'\0';
3162                 while (want > 0 && used != 0) {
3163                     const char *base = s;
3164                     reset_mbytes(state);
3165                     test = count_mbytes(base, 0, &state);
3166                     if (test == (size_t) -1) {
3167                         temp[--used] = 0;
3168                     } else if (test > want) {
3169                         temp[--used] = 0;
3170                     } else {
3171                         reset_mbytes(state);
3172                         trans_mbytes(buf, base, want, &state);
3173                         break;
3174                     }
3175                 }
3176                 free(temp);
3177             }
3178             wGet_wstring(stdscr, buf, SLKLEN);
3179             slk_wset((c - '0'), buf, fmt);
3180             slk_refresh();
3181             move(SLK_WORK, 0);
3182             clrtobot();
3183             break;
3184
3185         case case_QUIT:
3186             goto done;
3187
3188         case 'F':
3189             if (use_colors) {
3190                 fg = (NCURSES_COLOR_T) ((fg + 1) % COLORS);
3191                 call_slk_color(fg, bg);
3192             }
3193             break;
3194         case 'B':
3195             if (use_colors) {
3196                 bg = (NCURSES_COLOR_T) ((bg + 1) % COLORS);
3197                 call_slk_color(fg, bg);
3198             }
3199             break;
3200 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
3201         case KEY_RESIZE:
3202             wnoutrefresh(stdscr);
3203             break;
3204 #endif
3205         default:
3206             if (cycle_attr(c, &at_code, &attr, my_list, my_size)) {
3207                 slk_attr_set(attr, (NCURSES_COLOR_T) (fg || bg), NULL);
3208                 slk_touch();
3209                 slk_noutrefresh();
3210                 break;
3211             }
3212 #if HAVE_SLK_COLOR
3213             if (cycle_colors(c, &fg, &bg, &pair)) {
3214                 if (use_colors) {
3215                     call_slk_color(fg, bg);
3216                 } else {
3217                     beep();
3218                 }
3219                 break;
3220             }
3221 #endif
3222             beep();
3223             break;
3224         }
3225     } while (!isQuit(c = Getchar()));
3226
3227   done:
3228     slk_clear();
3229     erase();
3230     endwin();
3231 }
3232 #endif
3233 #endif /* SLK_INIT */
3234
3235 static void
3236 show_256_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3237 {
3238     unsigned first = 0;
3239     unsigned last = 255;
3240     unsigned code;
3241     int count;
3242
3243     erase();
3244     attron(A_BOLD);
3245     MvPrintw(0, 20, "Display of Character Codes %#0x to %#0x",
3246              first, last);
3247     attroff(A_BOLD);
3248     refresh();
3249
3250     for (code = first; code <= last; ++code) {
3251         int row = (int) (2 + (code / 16));
3252         int col = (int) (5 * (code % 16));
3253         IGNORE_RC(mvaddch(row, col, colored_chtype(code, attr, pair)));
3254         for (count = 1; count < repeat; ++count) {
3255             addch(colored_chtype(code, attr, pair));
3256         }
3257     }
3258
3259 }
3260
3261 /*
3262  * Show a slice of 32 characters, allowing those to be repeated up to the
3263  * screen's width.
3264  *
3265  * ISO 6429:  codes 0x80 to 0x9f may be control characters that cause the
3266  * terminal to perform functions.  The remaining codes can be graphic.
3267  */
3268 static void
3269 show_upper_chars(int base, int pagesize, int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3270 {
3271     unsigned code;
3272     unsigned first = (unsigned) base;
3273     unsigned last = first + (unsigned) pagesize - 2;
3274     bool C1 = (first == 128);
3275     int reply;
3276
3277     erase();
3278     attron(A_BOLD);
3279     MvPrintw(0, 20, "Display of %s Character Codes %d to %d",
3280              C1 ? "C1" : "GR", first, last);
3281     attroff(A_BOLD);
3282     refresh();
3283
3284     for (code = first; code <= last; code++) {
3285         int count = repeat;
3286         int row = 2 + ((int) (code - first) % (pagesize / 2));
3287         int col = ((int) (code - first) / (pagesize / 2)) * COLS / 2;
3288         char tmp[80];
3289         sprintf(tmp, "%3u (0x%x)", code, code);
3290         MvPrintw(row, col, "%*s: ", COLS / 4, tmp);
3291
3292         do {
3293             if (C1)
3294                 nodelay(stdscr, TRUE);
3295             echochar(colored_chtype(code, attr, pair));
3296             if (C1) {
3297                 /* (yes, this _is_ crude) */
3298                 while ((reply = Getchar()) != ERR) {
3299                     addch(UChar(reply));
3300                     napms(10);
3301                 }
3302                 nodelay(stdscr, FALSE);
3303             }
3304         } while (--count > 0);
3305     }
3306 }
3307
3308 #define PC_COLS 4
3309
3310 static void
3311 show_pc_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3312 {
3313     unsigned code;
3314
3315     erase();
3316     attron(A_BOLD);
3317     MvPrintw(0, 20, "Display of PC Character Codes");
3318     attroff(A_BOLD);
3319     refresh();
3320
3321     for (code = 0; code < 16; ++code) {
3322         MvPrintw(2, (int) code * PC_COLS + 8, "%X", code);
3323     }
3324     for (code = 0; code < 256; code++) {
3325         int count = repeat;
3326         int row = 3 + (int) (code / 16) + (code >= 128);
3327         int col = 8 + (int) (code % 16) * PC_COLS;
3328         if ((code % 16) == 0)
3329             MvPrintw(row, 0, "0x%02x:", code);
3330         move(row, col);
3331         do {
3332             switch (code) {
3333             case '\n':
3334             case '\r':
3335             case '\b':
3336             case '\f':
3337             case '\033':
3338             case 0x9b:
3339                 /*
3340                  * Skip the ones that do not work.
3341                  */
3342                 break;
3343             default:
3344                 addch(colored_chtype(code, A_ALTCHARSET | attr, pair));
3345                 break;
3346             }
3347         } while (--count > 0);
3348     }
3349 }
3350
3351 static void
3352 show_box_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3353 {
3354     (void) repeat;
3355
3356     attr |= (attr_t) COLOR_PAIR(pair);
3357
3358     erase();
3359     attron(A_BOLD);
3360     MvAddStr(0, 20, "Display of the ACS Line-Drawing Set");
3361     attroff(A_BOLD);
3362     refresh();
3363     /* *INDENT-OFF* */
3364     wborder(stdscr,
3365             colored_chtype(ACS_VLINE,    attr, pair),
3366             colored_chtype(ACS_VLINE,    attr, pair),
3367             colored_chtype(ACS_HLINE,    attr, pair),
3368             colored_chtype(ACS_HLINE,    attr, pair),
3369             colored_chtype(ACS_ULCORNER, attr, pair),
3370             colored_chtype(ACS_URCORNER, attr, pair),
3371             colored_chtype(ACS_LLCORNER, attr, pair),
3372             colored_chtype(ACS_LRCORNER, attr, pair));
3373     MvHLine(LINES / 2, 0,        colored_chtype(ACS_HLINE, attr, pair), COLS);
3374     MvVLine(0,         COLS / 2, colored_chtype(ACS_VLINE, attr, pair), LINES);
3375     MvAddCh(0,         COLS / 2, colored_chtype(ACS_TTEE,  attr, pair));
3376     MvAddCh(LINES / 2, COLS / 2, colored_chtype(ACS_PLUS,  attr, pair));
3377     MvAddCh(LINES - 1, COLS / 2, colored_chtype(ACS_BTEE,  attr, pair));
3378     MvAddCh(LINES / 2, 0,        colored_chtype(ACS_LTEE,  attr, pair));
3379     MvAddCh(LINES / 2, COLS - 1, colored_chtype(ACS_RTEE,  attr, pair));
3380     /* *INDENT-ON* */
3381
3382 }
3383
3384 static int
3385 show_1_acs(int n, int repeat, const char *name, chtype code)
3386 {
3387     const int height = 16;
3388     int row = 2 + (n % height);
3389     int col = (n / height) * COLS / 2;
3390
3391     MvPrintw(row, col, "%*s : ", COLS / 4, name);
3392     do {
3393         addch(code);
3394     } while (--repeat > 0);
3395     return n + 1;
3396 }
3397
3398 static void
3399 show_acs_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3400 /* display the ACS character set */
3401 {
3402     int n;
3403
3404 #define BOTH(name) #name, colored_chtype(name, attr, (chtype) pair)
3405
3406     erase();
3407     attron(A_BOLD);
3408     MvAddStr(0, 20, "Display of the ACS Character Set");
3409     attroff(A_BOLD);
3410     refresh();
3411
3412     n = show_1_acs(0, repeat, BOTH(ACS_ULCORNER));
3413     n = show_1_acs(n, repeat, BOTH(ACS_URCORNER));
3414     n = show_1_acs(n, repeat, BOTH(ACS_LLCORNER));
3415     n = show_1_acs(n, repeat, BOTH(ACS_LRCORNER));
3416
3417     n = show_1_acs(n, repeat, BOTH(ACS_LTEE));
3418     n = show_1_acs(n, repeat, BOTH(ACS_RTEE));
3419     n = show_1_acs(n, repeat, BOTH(ACS_TTEE));
3420     n = show_1_acs(n, repeat, BOTH(ACS_BTEE));
3421
3422     n = show_1_acs(n, repeat, BOTH(ACS_HLINE));
3423     n = show_1_acs(n, repeat, BOTH(ACS_VLINE));
3424
3425     /*
3426      * HPUX's ACS definitions are broken here.  Just give up.
3427      */
3428 #if !(defined(__hpux) && !defined(NCURSES_VERSION))
3429     n = show_1_acs(n, repeat, BOTH(ACS_LARROW));
3430     n = show_1_acs(n, repeat, BOTH(ACS_RARROW));
3431     n = show_1_acs(n, repeat, BOTH(ACS_UARROW));
3432     n = show_1_acs(n, repeat, BOTH(ACS_DARROW));
3433
3434     n = show_1_acs(n, repeat, BOTH(ACS_BLOCK));
3435     n = show_1_acs(n, repeat, BOTH(ACS_BOARD));
3436     n = show_1_acs(n, repeat, BOTH(ACS_LANTERN));
3437     n = show_1_acs(n, repeat, BOTH(ACS_BULLET));
3438     n = show_1_acs(n, repeat, BOTH(ACS_CKBOARD));
3439     n = show_1_acs(n, repeat, BOTH(ACS_DEGREE));
3440     n = show_1_acs(n, repeat, BOTH(ACS_DIAMOND));
3441     n = show_1_acs(n, repeat, BOTH(ACS_PLMINUS));
3442     n = show_1_acs(n, repeat, BOTH(ACS_PLUS));
3443
3444     n = show_1_acs(n, repeat, BOTH(ACS_GEQUAL));
3445     n = show_1_acs(n, repeat, BOTH(ACS_NEQUAL));
3446     n = show_1_acs(n, repeat, BOTH(ACS_LEQUAL));
3447
3448     n = show_1_acs(n, repeat, BOTH(ACS_STERLING));
3449     n = show_1_acs(n, repeat, BOTH(ACS_PI));
3450     n = show_1_acs(n, repeat, BOTH(ACS_S1));
3451     n = show_1_acs(n, repeat, BOTH(ACS_S3));
3452     n = show_1_acs(n, repeat, BOTH(ACS_S7));
3453     (void) show_1_acs(n, repeat, BOTH(ACS_S9));
3454 #endif
3455 }
3456
3457 static void
3458 acs_display(void)
3459 {
3460     int c = 'a';
3461     int pagesize = 32;
3462     char *term = getenv("TERM");
3463     const char *pch_kludge = ((term != 0 && strstr(term, "linux"))
3464                               ? "p=PC, "
3465                               : "");
3466     chtype attr = A_NORMAL;
3467     int digit = 0;
3468     int repeat = 1;
3469     int fg = COLOR_BLACK;
3470     int bg = COLOR_BLACK;
3471     unsigned at_code = 0;
3472     NCURSES_PAIRS_T pair = 0;
3473     void (*last_show_acs) (int, attr_t, NCURSES_PAIRS_T) = 0;
3474     ATTR_TBL my_list[SIZEOF(attrs_to_test)];
3475     unsigned my_size = init_attr_list(my_list, termattrs());
3476
3477     do {
3478         switch (c) {
3479         case CTRL('L'):
3480             Repaint();
3481             break;
3482         case 'a':
3483             ToggleAcs(last_show_acs, show_acs_chars);
3484             break;
3485         case 'p':
3486             if (*pch_kludge)
3487                 ToggleAcs(last_show_acs, show_pc_chars);
3488             else
3489                 beep();
3490             break;
3491         case 'w':
3492             if (pagesize == 32) {
3493                 pagesize = 256;
3494             } else {
3495                 pagesize = 32;
3496             }
3497             break;
3498         case 'x':
3499             ToggleAcs(last_show_acs, show_box_chars);
3500             break;
3501         case '0':
3502         case '1':
3503         case '2':
3504         case '3':
3505             digit = (c - '0');
3506             last_show_acs = 0;
3507             break;
3508         case '-':
3509             if (digit > 0) {
3510                 --digit;
3511                 last_show_acs = 0;
3512             } else {
3513                 beep();
3514             }
3515             break;
3516         case '+':
3517             if (digit < 3) {
3518                 ++digit;
3519                 last_show_acs = 0;
3520             } else {
3521                 beep();
3522             }
3523             break;
3524         case '>':
3525             if (repeat < (COLS / 4))
3526                 ++repeat;
3527             break;
3528         case '<':
3529             if (repeat > 1)
3530                 --repeat;
3531             break;
3532         default:
3533             if (cycle_attr(c, &at_code, &attr, my_list, my_size)
3534                 || cycle_colors(c, &fg, &bg, &pair)) {
3535                 break;
3536             } else {
3537                 beep();
3538             }
3539             break;
3540         }
3541         if (pagesize != 32) {
3542             show_256_chars(repeat, attr, pair);
3543         } else if (last_show_acs != 0) {
3544             last_show_acs(repeat, attr, pair);
3545         } else {
3546             show_upper_chars(digit * pagesize + 128, pagesize, repeat, attr, pair);
3547         }
3548
3549         MvPrintw(LINES - 3, 0,
3550                  "Note: ANSI terminals may not display C1 characters.");
3551         MvPrintw(LINES - 2, 0,
3552                  "Select: a=ACS, w=all x=box, %s0=C1, 1-3,+/- non-ASCII, </> repeat, ESC=quit",
3553                  pch_kludge);
3554         if (use_colors) {
3555             MvPrintw(LINES - 1, 0,
3556                      "v/V, f/F, b/B cycle through video attributes (%s) and color %d/%d.",
3557                      my_list[at_code].name,
3558                      fg, bg);
3559         } else {
3560             MvPrintw(LINES - 1, 0,
3561                      "v/V cycles through video attributes (%s).",
3562                      my_list[at_code].name);
3563         }
3564         refresh();
3565     } while (!isQuit(c = Getchar()));
3566
3567     Pause();
3568     erase();
3569     endwin();
3570 }
3571
3572 #if USE_WIDEC_SUPPORT
3573 static cchar_t *
3574 merge_wide_attr(cchar_t *dst, const cchar_t *src, attr_t attr, NCURSES_PAIRS_T pair)
3575 {
3576     int count;
3577
3578     *dst = *src;
3579     do {
3580         TEST_CCHAR(src, count, {
3581             attr |= (test_attrs & A_ALTCHARSET);
3582             setcchar(dst, test_wch, attr, pair, NULL);
3583         }
3584         , {
3585             ;
3586         });
3587     } while (0);
3588     return dst;
3589 }
3590
3591 /*
3592  * Header/legend take up no more than 8 lines, leaving 16 lines on a 24-line
3593  * display.  If there are no repeats, we could normally display 16 lines of 64
3594  * characters (1024 total).  However, taking repeats and double-width cells
3595  * into account, use 256 characters for the page.
3596  */
3597 static void
3598 show_paged_widechars(int base,
3599                      int pagesize,
3600                      int repeat,
3601                      int space,
3602                      attr_t attr,
3603                      NCURSES_PAIRS_T pair)
3604 {
3605     int first = base * pagesize;
3606     int last = first + pagesize - 1;
3607     int per_line = 16;
3608     cchar_t temp;
3609     wchar_t code;
3610     wchar_t codes[10];
3611
3612     erase();
3613     attron(A_BOLD);
3614     MvPrintw(0, 20, "Display of Character Codes %#x to %#x", first, last);
3615     attroff(A_BOLD);
3616
3617     for (code = (wchar_t) first; (int) code <= last; code++) {
3618         int row = (2 + ((int) code - first) / per_line);
3619         int col = 5 * ((int) code % per_line);
3620         int count;
3621
3622         memset(&codes, 0, sizeof(codes));
3623         codes[0] = code;
3624         setcchar(&temp, codes, attr, pair, 0);
3625         move(row, col);
3626         if (wcwidth(code) == 0 && code != 0) {
3627             addch((chtype) space |
3628                   (A_REVERSE ^ attr) |
3629                   (attr_t) COLOR_PAIR(pair));
3630         }
3631         add_wch(&temp);
3632         for (count = 1; count < repeat; ++count) {
3633             add_wch(&temp);
3634         }
3635     }
3636 }
3637
3638 static void
3639 show_upper_widechars(int first, int repeat, int space, attr_t attr, NCURSES_PAIRS_T pair)
3640 {
3641     cchar_t temp;
3642     wchar_t code;
3643     int last = first + 31;
3644
3645     erase();
3646     attron(A_BOLD);
3647     MvPrintw(0, 20, "Display of Character Codes %d to %d", first, last);
3648     attroff(A_BOLD);
3649
3650     for (code = (wchar_t) first; (int) code <= last; code++) {
3651         int row = 2 + ((code - first) % 16);
3652         int col = ((code - first) / 16) * COLS / 2;
3653         wchar_t codes[10];
3654         char tmp[80];
3655         int count = repeat;
3656         int y, x;
3657
3658         sprintf(tmp, "%3ld (0x%lx)", (long) code, (long) code);
3659         MvPrintw(row, col, "%*s: ", COLS / 4, tmp);
3660
3661         memset(&codes, 0, sizeof(codes));
3662         codes[0] = code;
3663         setcchar(&temp, codes, attr, pair, 0);
3664
3665         do {
3666             /*
3667              * Give non-spacing characters something to combine with.  If we
3668              * don't, they'll bunch up in a heap on the space after the ":".
3669              * Mark them with reverse-video to make them simpler to find on
3670              * the display.
3671              */
3672             if (wcwidth(code) == 0) {
3673                 addch((chtype) space |
3674                       (A_REVERSE ^ attr) |
3675                       (attr_t) COLOR_PAIR(pair));
3676             }
3677             /*
3678              * This uses echo_wchar(), for comparison with the normal 'f'
3679              * test (and to make a test-case for echo_wchar()).  The screen
3680              * may flicker because the erase() at the top of the function
3681              * is met by the builtin refresh() in echo_wchar().
3682              */
3683             echo_wchar(&temp);
3684             /*
3685              * The repeat-count may make text wrap - avoid that.
3686              */
3687             getyx(stdscr, y, x);
3688             (void) y;
3689             if (x >= col + (COLS / 2) - 2)
3690                 break;
3691         } while (--count > 0);
3692     }
3693 }
3694
3695 static int
3696 show_1_wacs(int n, int repeat, const char *name, const cchar_t *code)
3697 {
3698     const int height = 16;
3699     int row = 2 + (n % height);
3700     int col = (n / height) * COLS / 2;
3701
3702     MvPrintw(row, col, "%*s : ", COLS / 4, name);
3703     while (--repeat >= 0) {
3704         add_wch(code);
3705     }
3706     return n + 1;
3707 }
3708
3709 #define MERGE_ATTR(wch) merge_wide_attr(&temp, wch, attr, pair)
3710
3711 static void
3712 show_wacs_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3713 /* display the wide-ACS character set */
3714 {
3715     cchar_t temp;
3716
3717     int n;
3718
3719 /*#define BOTH2(name) #name, &(name) */
3720 #define BOTH2(name) #name, MERGE_ATTR(name)
3721
3722     erase();
3723     attron(A_BOLD);
3724     MvAddStr(0, 20, "Display of the Wide-ACS Character Set");
3725     attroff(A_BOLD);
3726     refresh();
3727
3728     n = show_1_wacs(0, repeat, BOTH2(WACS_ULCORNER));
3729     n = show_1_wacs(n, repeat, BOTH2(WACS_URCORNER));
3730     n = show_1_wacs(n, repeat, BOTH2(WACS_LLCORNER));
3731     n = show_1_wacs(n, repeat, BOTH2(WACS_LRCORNER));
3732
3733     n = show_1_wacs(n, repeat, BOTH2(WACS_LTEE));
3734     n = show_1_wacs(n, repeat, BOTH2(WACS_RTEE));
3735     n = show_1_wacs(n, repeat, BOTH2(WACS_TTEE));
3736     n = show_1_wacs(n, repeat, BOTH2(WACS_BTEE));
3737
3738     n = show_1_wacs(n, repeat, BOTH2(WACS_HLINE));
3739     n = show_1_wacs(n, repeat, BOTH2(WACS_VLINE));
3740
3741     n = show_1_wacs(n, repeat, BOTH2(WACS_LARROW));
3742     n = show_1_wacs(n, repeat, BOTH2(WACS_RARROW));
3743     n = show_1_wacs(n, repeat, BOTH2(WACS_UARROW));
3744     n = show_1_wacs(n, repeat, BOTH2(WACS_DARROW));
3745
3746     n = show_1_wacs(n, repeat, BOTH2(WACS_BLOCK));
3747     n = show_1_wacs(n, repeat, BOTH2(WACS_BOARD));
3748     n = show_1_wacs(n, repeat, BOTH2(WACS_LANTERN));
3749     n = show_1_wacs(n, repeat, BOTH2(WACS_BULLET));
3750     n = show_1_wacs(n, repeat, BOTH2(WACS_CKBOARD));
3751     n = show_1_wacs(n, repeat, BOTH2(WACS_DEGREE));
3752     n = show_1_wacs(n, repeat, BOTH2(WACS_DIAMOND));
3753     n = show_1_wacs(n, repeat, BOTH2(WACS_PLMINUS));
3754     n = show_1_wacs(n, repeat, BOTH2(WACS_PLUS));
3755
3756 #ifdef CURSES_WACS_ARRAY
3757     n = show_1_wacs(n, repeat, BOTH2(WACS_GEQUAL));
3758     n = show_1_wacs(n, repeat, BOTH2(WACS_NEQUAL));
3759     n = show_1_wacs(n, repeat, BOTH2(WACS_LEQUAL));
3760
3761     n = show_1_wacs(n, repeat, BOTH2(WACS_STERLING));
3762     n = show_1_wacs(n, repeat, BOTH2(WACS_PI));
3763     n = show_1_wacs(n, repeat, BOTH2(WACS_S1));
3764     n = show_1_wacs(n, repeat, BOTH2(WACS_S3));
3765     n = show_1_wacs(n, repeat, BOTH2(WACS_S7));
3766     (void) show_1_wacs(n, repeat, BOTH2(WACS_S9));
3767 #endif
3768 }
3769
3770 #ifdef WACS_D_PLUS
3771 static void
3772 show_wacs_chars_double(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3773 /* display the wide-ACS character set */
3774 {
3775     cchar_t temp;
3776
3777     int n;
3778
3779 /*#define BOTH2(name) #name, &(name) */
3780 #define BOTH2(name) #name, MERGE_ATTR(name)
3781
3782     erase();
3783     attron(A_BOLD);
3784     MvAddStr(0, 20, "Display of the Wide-ACS Character Set");
3785     attroff(A_BOLD);
3786     refresh();
3787
3788     n = show_1_wacs(0, repeat, BOTH2(WACS_D_ULCORNER));
3789     n = show_1_wacs(n, repeat, BOTH2(WACS_D_URCORNER));
3790     n = show_1_wacs(n, repeat, BOTH2(WACS_D_LLCORNER));
3791     n = show_1_wacs(n, repeat, BOTH2(WACS_D_LRCORNER));
3792
3793     n = show_1_wacs(n, repeat, BOTH2(WACS_D_LTEE));
3794     n = show_1_wacs(n, repeat, BOTH2(WACS_D_RTEE));
3795     n = show_1_wacs(n, repeat, BOTH2(WACS_D_TTEE));
3796     n = show_1_wacs(n, repeat, BOTH2(WACS_D_BTEE));
3797
3798     n = show_1_wacs(n, repeat, BOTH2(WACS_D_HLINE));
3799     n = show_1_wacs(n, repeat, BOTH2(WACS_D_VLINE));
3800
3801     n = show_1_wacs(n, repeat, BOTH2(WACS_LARROW));
3802     n = show_1_wacs(n, repeat, BOTH2(WACS_RARROW));
3803     n = show_1_wacs(n, repeat, BOTH2(WACS_UARROW));
3804     n = show_1_wacs(n, repeat, BOTH2(WACS_DARROW));
3805
3806     n = show_1_wacs(n, repeat, BOTH2(WACS_BLOCK));
3807     n = show_1_wacs(n, repeat, BOTH2(WACS_BOARD));
3808     n = show_1_wacs(n, repeat, BOTH2(WACS_LANTERN));
3809     n = show_1_wacs(n, repeat, BOTH2(WACS_BULLET));
3810     n = show_1_wacs(n, repeat, BOTH2(WACS_CKBOARD));
3811     n = show_1_wacs(n, repeat, BOTH2(WACS_DEGREE));
3812     n = show_1_wacs(n, repeat, BOTH2(WACS_DIAMOND));
3813     n = show_1_wacs(n, repeat, BOTH2(WACS_PLMINUS));
3814     n = show_1_wacs(n, repeat, BOTH2(WACS_PLUS));
3815
3816 #ifdef CURSES_WACS_ARRAY
3817     n = show_1_wacs(n, repeat, BOTH2(WACS_GEQUAL));
3818     n = show_1_wacs(n, repeat, BOTH2(WACS_NEQUAL));
3819     n = show_1_wacs(n, repeat, BOTH2(WACS_LEQUAL));
3820
3821     n = show_1_wacs(n, repeat, BOTH2(WACS_STERLING));
3822     n = show_1_wacs(n, repeat, BOTH2(WACS_PI));
3823     n = show_1_wacs(n, repeat, BOTH2(WACS_S1));
3824     n = show_1_wacs(n, repeat, BOTH2(WACS_S3));
3825     n = show_1_wacs(n, repeat, BOTH2(WACS_S7));
3826     (void) show_1_wacs(n, repeat, BOTH2(WACS_S9));
3827 #endif
3828 }
3829 #endif
3830
3831 #ifdef WACS_T_PLUS
3832 static void
3833 show_wacs_chars_thick(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3834 /* display the wide-ACS character set */
3835 {
3836     cchar_t temp;
3837
3838     int n;
3839
3840 /*#define BOTH2(name) #name, &(name) */
3841 #define BOTH2(name) #name, MERGE_ATTR(name)
3842
3843     erase();
3844     attron(A_BOLD);
3845     MvAddStr(0, 20, "Display of the Wide-ACS Character Set");
3846     attroff(A_BOLD);
3847     refresh();
3848
3849     n = show_1_wacs(0, repeat, BOTH2(WACS_T_ULCORNER));
3850     n = show_1_wacs(n, repeat, BOTH2(WACS_T_URCORNER));
3851     n = show_1_wacs(n, repeat, BOTH2(WACS_T_LLCORNER));
3852     n = show_1_wacs(n, repeat, BOTH2(WACS_T_LRCORNER));
3853
3854     n = show_1_wacs(n, repeat, BOTH2(WACS_T_LTEE));
3855     n = show_1_wacs(n, repeat, BOTH2(WACS_T_RTEE));
3856     n = show_1_wacs(n, repeat, BOTH2(WACS_T_TTEE));
3857     n = show_1_wacs(n, repeat, BOTH2(WACS_T_BTEE));
3858
3859     n = show_1_wacs(n, repeat, BOTH2(WACS_T_HLINE));
3860     n = show_1_wacs(n, repeat, BOTH2(WACS_T_VLINE));
3861
3862     n = show_1_wacs(n, repeat, BOTH2(WACS_LARROW));
3863     n = show_1_wacs(n, repeat, BOTH2(WACS_RARROW));
3864     n = show_1_wacs(n, repeat, BOTH2(WACS_UARROW));
3865     n = show_1_wacs(n, repeat, BOTH2(WACS_DARROW));
3866
3867     n = show_1_wacs(n, repeat, BOTH2(WACS_BLOCK));
3868     n = show_1_wacs(n, repeat, BOTH2(WACS_BOARD));
3869     n = show_1_wacs(n, repeat, BOTH2(WACS_LANTERN));
3870     n = show_1_wacs(n, repeat, BOTH2(WACS_BULLET));
3871     n = show_1_wacs(n, repeat, BOTH2(WACS_CKBOARD));
3872     n = show_1_wacs(n, repeat, BOTH2(WACS_DEGREE));
3873     n = show_1_wacs(n, repeat, BOTH2(WACS_DIAMOND));
3874     n = show_1_wacs(n, repeat, BOTH2(WACS_PLMINUS));
3875     n = show_1_wacs(n, repeat, BOTH2(WACS_PLUS));
3876
3877 #ifdef CURSES_WACS_ARRAY
3878     n = show_1_wacs(n, repeat, BOTH2(WACS_GEQUAL));
3879     n = show_1_wacs(n, repeat, BOTH2(WACS_NEQUAL));
3880     n = show_1_wacs(n, repeat, BOTH2(WACS_LEQUAL));
3881
3882     n = show_1_wacs(n, repeat, BOTH2(WACS_STERLING));
3883     n = show_1_wacs(n, repeat, BOTH2(WACS_PI));
3884     n = show_1_wacs(n, repeat, BOTH2(WACS_S1));
3885     n = show_1_wacs(n, repeat, BOTH2(WACS_S3));
3886     n = show_1_wacs(n, repeat, BOTH2(WACS_S7));
3887     (void) show_1_wacs(n, repeat, BOTH2(WACS_S9));
3888 #endif
3889 }
3890 #endif
3891
3892 #undef MERGE_ATTR
3893
3894 #define MERGE_ATTR(n,wch) merge_wide_attr(&temp[n], wch, attr, pair)
3895
3896 static void
3897 show_wbox_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3898 {
3899     cchar_t temp[8];
3900
3901     (void) repeat;
3902     erase();
3903     attron(A_BOLD);
3904     MvAddStr(0, 20, "Display of the Wide-ACS Line-Drawing Set");
3905     attroff(A_BOLD);
3906     refresh();
3907
3908     wborder_set(stdscr,
3909                 MERGE_ATTR(0, WACS_VLINE),
3910                 MERGE_ATTR(1, WACS_VLINE),
3911                 MERGE_ATTR(2, WACS_HLINE),
3912                 MERGE_ATTR(3, WACS_HLINE),
3913                 MERGE_ATTR(4, WACS_ULCORNER),
3914                 MERGE_ATTR(5, WACS_URCORNER),
3915                 MERGE_ATTR(6, WACS_LLCORNER),
3916                 MERGE_ATTR(7, WACS_LRCORNER));
3917     /* *INDENT-OFF* */
3918     (void) mvhline_set(LINES / 2, 0,        MERGE_ATTR(0, WACS_HLINE), COLS);
3919     (void) mvvline_set(0,         COLS / 2, MERGE_ATTR(0, WACS_VLINE), LINES);
3920     (void) mvadd_wch(0,           COLS / 2, MERGE_ATTR(0, WACS_TTEE));
3921     (void) mvadd_wch(LINES / 2,   COLS / 2, MERGE_ATTR(0, WACS_PLUS));
3922     (void) mvadd_wch(LINES - 1,   COLS / 2, MERGE_ATTR(0, WACS_BTEE));
3923     (void) mvadd_wch(LINES / 2,   0,        MERGE_ATTR(0, WACS_LTEE));
3924     (void) mvadd_wch(LINES / 2,   COLS - 1, MERGE_ATTR(0, WACS_RTEE));
3925     /* *INDENT-ON* */
3926
3927 }
3928
3929 #undef MERGE_ATTR
3930
3931 static int
3932 show_2_wacs(int n, const char *name, const char *code, attr_t attr, NCURSES_PAIRS_T pair)
3933 {
3934     const int height = 16;
3935     int row = 2 + (n % height);
3936     int col = (n / height) * COLS / 2;
3937     char temp[80];
3938
3939     MvPrintw(row, col, "%*s : ", COLS / 4, name);
3940     (void) attr_set(attr, pair, 0);
3941     addstr(strncpy(temp, code, 20));
3942     (void) attr_set(A_NORMAL, 0, 0);
3943     return n + 1;
3944 }
3945
3946 #define SHOW_UTF8(n, name, code) show_2_wacs(n, name, code, attr, pair)
3947
3948 static void
3949 show_utf8_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3950 {
3951     int n;
3952
3953     (void) repeat;
3954     erase();
3955     attron(A_BOLD);
3956     MvAddStr(0, 20, "Display of the Wide-ACS Character Set");
3957     attroff(A_BOLD);
3958     refresh();
3959     /* *INDENT-OFF* */
3960     n = SHOW_UTF8(0, "WACS_ULCORNER",   "\342\224\214");
3961     n = SHOW_UTF8(n, "WACS_URCORNER",   "\342\224\220");
3962     n = SHOW_UTF8(n, "WACS_LLCORNER",   "\342\224\224");
3963     n = SHOW_UTF8(n, "WACS_LRCORNER",   "\342\224\230");
3964
3965     n = SHOW_UTF8(n, "WACS_LTEE",       "\342\224\234");
3966     n = SHOW_UTF8(n, "WACS_RTEE",       "\342\224\244");
3967     n = SHOW_UTF8(n, "WACS_TTEE",       "\342\224\254");
3968     n = SHOW_UTF8(n, "WACS_BTEE",       "\342\224\264");
3969
3970     n = SHOW_UTF8(n, "WACS_HLINE",      "\342\224\200");
3971     n = SHOW_UTF8(n, "WACS_VLINE",      "\342\224\202");
3972
3973     n = SHOW_UTF8(n, "WACS_LARROW",     "\342\206\220");
3974     n = SHOW_UTF8(n, "WACS_RARROW",     "\342\206\222");
3975     n = SHOW_UTF8(n, "WACS_UARROW",     "\342\206\221");
3976     n = SHOW_UTF8(n, "WACS_DARROW",     "\342\206\223");
3977
3978     n = SHOW_UTF8(n, "WACS_BLOCK",      "\342\226\256");
3979     n = SHOW_UTF8(n, "WACS_BOARD",      "\342\226\222");
3980     n = SHOW_UTF8(n, "WACS_LANTERN",    "\342\230\203");
3981     n = SHOW_UTF8(n, "WACS_BULLET",     "\302\267");
3982     n = SHOW_UTF8(n, "WACS_CKBOARD",    "\342\226\222");
3983     n = SHOW_UTF8(n, "WACS_DEGREE",     "\302\260");
3984     n = SHOW_UTF8(n, "WACS_DIAMOND",    "\342\227\206");
3985     n = SHOW_UTF8(n, "WACS_PLMINUS",    "\302\261");
3986     n = SHOW_UTF8(n, "WACS_PLUS",       "\342\224\274");
3987     n = SHOW_UTF8(n, "WACS_GEQUAL",     "\342\211\245");
3988     n = SHOW_UTF8(n, "WACS_NEQUAL",     "\342\211\240");
3989     n = SHOW_UTF8(n, "WACS_LEQUAL",     "\342\211\244");
3990
3991     n = SHOW_UTF8(n, "WACS_STERLING",   "\302\243");
3992     n = SHOW_UTF8(n, "WACS_PI",         "\317\200");
3993     n = SHOW_UTF8(n, "WACS_S1",         "\342\216\272");
3994     n = SHOW_UTF8(n, "WACS_S3",         "\342\216\273");
3995     n = SHOW_UTF8(n, "WACS_S7",         "\342\216\274");
3996     (void) SHOW_UTF8(n, "WACS_S9",      "\342\216\275");
3997     /* *INDENT-ON* */
3998
3999 }
4000
4001 /* display the wide-ACS character set */
4002 static void
4003 wide_acs_display(void)
4004 {
4005     int c = 'a';
4006     int digit = 0;
4007     int repeat = 1;
4008     int space = ' ';
4009     int pagesize = 32;
4010     chtype attr = A_NORMAL;
4011     int fg = COLOR_BLACK;
4012     int bg = COLOR_BLACK;
4013     unsigned at_code = 0;
4014     NCURSES_PAIRS_T pair = 0;
4015     void (*last_show_wacs) (int, attr_t, NCURSES_PAIRS_T) = 0;
4016     ATTR_TBL my_list[SIZEOF(attrs_to_test)];
4017     unsigned my_size = init_attr_list(my_list, term_attrs());
4018
4019     do {
4020         switch (c) {
4021         case CTRL('L'):
4022             Repaint();
4023             break;
4024         case 'a':
4025             ToggleAcs(last_show_wacs, show_wacs_chars);
4026             break;
4027 #ifdef WACS_D_PLUS
4028         case 'd':
4029             ToggleAcs(last_show_wacs, show_wacs_chars_double);
4030             break;
4031 #endif
4032 #ifdef WACS_T_PLUS
4033         case 't':
4034             ToggleAcs(last_show_wacs, show_wacs_chars_thick);
4035             break;
4036 #endif
4037         case 'w':
4038             if (pagesize == 32) {
4039                 pagesize = 256;
4040             } else {
4041                 pagesize = 32;
4042             }
4043             break;
4044         case 'x':
4045             ToggleAcs(last_show_wacs, show_wbox_chars);
4046             break;
4047         case 'u':
4048             ToggleAcs(last_show_wacs, show_utf8_chars);
4049             break;
4050         default:
4051             if (c < 256 && isdigit(c)) {
4052                 digit = (c - '0');
4053                 last_show_wacs = 0;
4054             } else if (c == '+') {
4055                 ++digit;
4056                 last_show_wacs = 0;
4057             } else if (c == '-' && digit > 0) {
4058                 --digit;
4059                 last_show_wacs = 0;
4060             } else if (c == '>' && repeat < (COLS / 4)) {
4061                 ++repeat;
4062             } else if (c == '<' && repeat > 1) {
4063                 --repeat;
4064             } else if (c == '_') {
4065                 space = (space == ' ') ? '_' : ' ';
4066                 last_show_wacs = 0;
4067             } else if (cycle_attr(c, &at_code, &attr, my_list, my_size)
4068                        || cycle_colors(c, &fg, &bg, &pair)) {
4069                 if (last_show_wacs != 0)
4070                     break;
4071             } else {
4072                 beep();
4073                 break;
4074             }
4075             break;
4076         }
4077         if (pagesize != 32) {
4078             show_paged_widechars(digit, pagesize, repeat, space, attr, pair);
4079         } else if (last_show_wacs != 0) {
4080             last_show_wacs(repeat, attr, pair);
4081         } else {
4082             show_upper_widechars(digit * 32 + 128, repeat, space, attr, pair);
4083         }
4084
4085         MvPrintw(LINES - 4, 0,
4086                  "Select: a/d/t WACS, w=all x=box, u UTF-8, ^L repaint");
4087         MvPrintw(LINES - 3, 2,
4088                  "0-9,+/- non-ASCII, </> repeat, _ space, ESC=quit");
4089         if (use_colors) {
4090             MvPrintw(LINES - 2, 2,
4091                      "v/V, f/F, b/B cycle through video attributes (%s) and color %d/%d.",
4092                      my_list[at_code].name,
4093                      fg, bg);
4094         } else {
4095             MvPrintw(LINES - 2, 2,
4096                      "v/V cycles through video attributes (%s).",
4097                      my_list[at_code].name);
4098         }
4099         refresh();
4100     } while (!isQuit(c = Getchar()));
4101
4102     Pause();
4103     erase();
4104     endwin();
4105 }
4106
4107 #endif
4108
4109 /*
4110  * Graphic-rendition test (adapted from vttest)
4111  */
4112 static void
4113 test_sgr_attributes(void)
4114 {
4115     int pass;
4116
4117     for (pass = 0; pass < 2; pass++) {
4118         chtype normal = ((pass == 0 ? A_NORMAL : A_REVERSE)) | BLANK;
4119
4120         /* Use non-default colors if possible to exercise bce a little */
4121         if (use_colors) {
4122             init_pair(1, COLOR_WHITE, COLOR_BLUE);
4123             normal |= (chtype) COLOR_PAIR(1);
4124         }
4125         bkgdset(normal);
4126         erase();
4127         MvPrintw(1, 20, "Graphic rendition test pattern:");
4128
4129         MvPrintw(4, 1, "vanilla");
4130
4131 #define set_sgr(mask) bkgdset((normal^(mask)));
4132         set_sgr(A_BOLD);
4133         MvPrintw(4, 40, "bold");
4134
4135         set_sgr(A_UNDERLINE);
4136         MvPrintw(6, 6, "underline");
4137
4138         set_sgr(A_BOLD | A_UNDERLINE);
4139         MvPrintw(6, 45, "bold underline");
4140
4141         set_sgr(A_BLINK);
4142         MvPrintw(8, 1, "blink");
4143
4144         set_sgr(A_BLINK | A_BOLD);
4145         MvPrintw(8, 40, "bold blink");
4146
4147         set_sgr(A_UNDERLINE | A_BLINK);
4148         MvPrintw(10, 6, "underline blink");
4149
4150         set_sgr(A_BOLD | A_UNDERLINE | A_BLINK);
4151         MvPrintw(10, 45, "bold underline blink");
4152
4153         set_sgr(A_REVERSE);
4154         MvPrintw(12, 1, "negative");
4155
4156         set_sgr(A_BOLD | A_REVERSE);
4157         MvPrintw(12, 40, "bold negative");
4158
4159         set_sgr(A_UNDERLINE | A_REVERSE);
4160         MvPrintw(14, 6, "underline negative");
4161
4162         set_sgr(A_BOLD | A_UNDERLINE | A_REVERSE);
4163         MvPrintw(14, 45, "bold underline negative");
4164
4165         set_sgr(A_BLINK | A_REVERSE);
4166         MvPrintw(16, 1, "blink negative");
4167
4168         set_sgr(A_BOLD | A_BLINK | A_REVERSE);
4169         MvPrintw(16, 40, "bold blink negative");
4170
4171         set_sgr(A_UNDERLINE | A_BLINK | A_REVERSE);
4172         MvPrintw(18, 6, "underline blink negative");
4173
4174         set_sgr(A_BOLD | A_UNDERLINE | A_BLINK | A_REVERSE);
4175         MvPrintw(18, 45, "bold underline blink negative");
4176
4177         bkgdset(normal);
4178         MvPrintw(LINES - 2, 1, "%s background. ", pass == 0 ? "Dark" :
4179                  "Light");
4180         clrtoeol();
4181         Pause();
4182     }
4183
4184     bkgdset(A_NORMAL | BLANK);
4185     erase();
4186     endwin();
4187 }
4188
4189 /****************************************************************************
4190  *
4191  * Windows and scrolling tester.
4192  *
4193  ****************************************************************************/
4194
4195 #define BOTLINES        4       /* number of line stolen from screen bottom */
4196
4197 typedef struct {
4198     int y, x;
4199 } pair;
4200
4201 #define FRAME struct frame
4202 FRAME
4203 {
4204     FRAME *next, *last;
4205     bool do_scroll;
4206     bool do_keypad;
4207     WINDOW *wind;
4208 };
4209
4210 #if defined(NCURSES_VERSION)
4211 #if (NCURSES_VERSION_PATCH < 20070331) && NCURSES_EXT_FUNCS
4212 #define is_keypad(win)   (win)->_use_keypad
4213 #define is_scrollok(win) (win)->_scroll
4214 #elif !defined(is_keypad)
4215 #define is_keypad(win)   FALSE
4216 #define is_scrollok(win) FALSE
4217 #endif
4218 #else
4219 #define is_keypad(win)   FALSE
4220 #define is_scrollok(win) FALSE
4221 #endif
4222
4223 static WINDOW *
4224 frame_win(FRAME * curp)
4225 {
4226     return (curp != 0) ? curp->wind : stdscr;
4227 }
4228
4229 /* We need to know if these flags are actually set, so don't look in FRAME.
4230  * These names are known to work with SVr4 curses as well as ncurses.  The
4231  * _use_keypad name does not work with Solaris 8.
4232  */
4233 static bool
4234 HaveKeypad(FRAME * curp)
4235 {
4236     WINDOW *win = frame_win(curp);
4237     (void) win;
4238     return is_keypad(win);
4239 }
4240
4241 static bool
4242 HaveScroll(FRAME * curp)
4243 {
4244     WINDOW *win = frame_win(curp);
4245     (void) win;
4246     return is_scrollok(win);
4247 }
4248
4249 static void
4250 newwin_legend(FRAME * curp)
4251 {
4252     static const struct {
4253         const char *msg;
4254         int code;
4255     } legend[] = {
4256         {
4257             "^C = create window", 0
4258         },
4259         {
4260             "^N = next window", 0
4261         },
4262         {
4263             "^P = previous window", 0
4264         },
4265         {
4266             "^F = scroll forward", 0
4267         },
4268         {
4269             "^B = scroll backward", 0
4270         },
4271         {
4272             "^K = keypad(%s)", 1
4273         },
4274         {
4275             "^S = scrollok(%s)", 2
4276         },
4277         {
4278             "^W = save window to file", 0
4279         },
4280         {
4281             "^R = restore window", 0
4282         },
4283 #if HAVE_WRESIZE
4284         {
4285             "^X = resize", 0
4286         },
4287 #endif
4288         {
4289             "^Q%s = exit", 3
4290         }
4291     };
4292     size_t n;
4293     int x;
4294     bool do_keypad = HaveKeypad(curp);
4295     bool do_scroll = HaveScroll(curp);
4296     char buf[BUFSIZ];
4297
4298     move(LINES - 4, 0);
4299     for (n = 0; n < SIZEOF(legend); n++) {
4300         switch (legend[n].code) {
4301         default:
4302             strcpy(buf, legend[n].msg);
4303             break;
4304         case 1:
4305             sprintf(buf, legend[n].msg, do_keypad ? "yes" : "no");
4306             break;
4307         case 2:
4308             sprintf(buf, legend[n].msg, do_scroll ? "yes" : "no");
4309             break;
4310         case 3:
4311             sprintf(buf, legend[n].msg, do_keypad ? "/ESC" : "");
4312             break;
4313         }
4314         x = getcurx(stdscr);
4315         addstr((COLS < (x + 3 + (int) strlen(buf))) ? "\n" : (n ? ", " : ""));
4316         addstr(buf);
4317     }
4318     clrtoeol();
4319 }
4320
4321 static void
4322 transient(FRAME * curp, NCURSES_CONST char *msg)
4323 {
4324     newwin_legend(curp);
4325     if (msg) {
4326         MvAddStr(LINES - 1, 0, msg);
4327         refresh();
4328         napms(1000);
4329     }
4330
4331     move(LINES - 1, 0);
4332     printw("%s characters are echoed, window should %sscroll.",
4333            HaveKeypad(curp) ? "Non-arrow" : "All other",
4334            HaveScroll(curp) ? "" : "not ");
4335     clrtoeol();
4336 }
4337
4338 static void
4339 newwin_report(FRAME * curp)
4340 /* report on the cursor's current position, then restore it */
4341 {
4342     WINDOW *win = frame_win(curp);
4343     int y, x;
4344
4345     if (win != stdscr)
4346         transient(curp, (char *) 0);
4347     getyx(win, y, x);
4348     move(LINES - 1, COLS - 17);
4349     printw("Y = %2d X = %2d", y, x);
4350     if (win != stdscr)
4351         refresh();
4352     else
4353         wmove(win, y, x);
4354 }
4355
4356 static pair *
4357 selectcell(int uli, int ulj, int lri, int lrj)
4358 /* arrows keys move cursor, return location at current on non-arrow key */
4359 {
4360     static pair res;            /* result cell */
4361     int si = lri - uli + 1;     /* depth of the select area */
4362     int sj = lrj - ulj + 1;     /* width of the select area */
4363     int i = 0, j = 0;           /* offsets into the select area */
4364
4365     res.y = uli;
4366     res.x = ulj;
4367     for (;;) {
4368         move(uli + i, ulj + j);
4369         newwin_report((FRAME *) 0);
4370
4371         switch (Getchar()) {
4372         case KEY_UP:
4373             i += si - 1;
4374             break;
4375         case KEY_DOWN:
4376             i++;
4377             break;
4378         case KEY_LEFT:
4379             j += sj - 1;
4380             break;
4381         case KEY_RIGHT:
4382             j++;
4383             break;
4384         case case_QUIT:
4385             return ((pair *) 0);
4386 #ifdef NCURSES_MOUSE_VERSION
4387         case KEY_MOUSE:
4388             {
4389                 MEVENT event;
4390
4391                 getmouse(&event);
4392                 if (event.y > uli && event.x > ulj) {
4393                     i = event.y - uli;
4394                     j = event.x - ulj;
4395                 } else {
4396                     beep();
4397                     break;
4398                 }
4399             }
4400             /* FALLTHRU */
4401 #endif
4402         default:
4403             res.y = uli + i;
4404             res.x = ulj + j;
4405             return (&res);
4406         }
4407         i %= si;
4408         j %= sj;
4409     }
4410 }
4411
4412 static void
4413 outerbox(pair ul, pair lr, bool onoff)
4414 /* draw or erase a box *outside* the given pair of corners */
4415 {
4416     MvAddCh(ul.y - 1, lr.x - 1, onoff ? ACS_ULCORNER : ' ');
4417     MvAddCh(ul.y - 1, lr.x + 1, onoff ? ACS_URCORNER : ' ');
4418     MvAddCh(lr.y + 1, lr.x + 1, onoff ? ACS_LRCORNER : ' ');
4419     MvAddCh(lr.y + 1, ul.x - 1, onoff ? ACS_LLCORNER : ' ');
4420     move(ul.y - 1, ul.x);
4421     hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
4422     move(ul.y, ul.x - 1);
4423     vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
4424     move(lr.y + 1, ul.x);
4425     hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
4426     move(ul.y, lr.x + 1);
4427     vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
4428 }
4429
4430 static WINDOW *
4431 getwindow(void)
4432 /* Ask user for a window definition */
4433 {
4434     WINDOW *rwindow;
4435     pair ul, lr, *tmp;
4436
4437     move(0, 0);
4438     clrtoeol();
4439     addstr("Use arrows to move cursor, anything else to mark corner 1");
4440     refresh();
4441     if ((tmp = selectcell(2, 1, LINES - BOTLINES - 2, COLS - 2)) == (pair *) 0)
4442         return ((WINDOW *) 0);
4443     memcpy(&ul, tmp, sizeof(pair));
4444     MvAddCh(ul.y - 1, ul.x - 1, ACS_ULCORNER);
4445     move(0, 0);
4446     clrtoeol();
4447     addstr("Use arrows to move cursor, anything else to mark corner 2");
4448     refresh();
4449     if ((tmp = selectcell(ul.y, ul.x, LINES - BOTLINES - 2, COLS - 2)) ==
4450         (pair *) 0)
4451         return ((WINDOW *) 0);
4452     memcpy(&lr, tmp, sizeof(pair));
4453
4454     rwindow = subwin(stdscr, lr.y - ul.y + 1, lr.x - ul.x + 1, ul.y, ul.x);
4455
4456     outerbox(ul, lr, TRUE);
4457     refresh();
4458
4459     if (rwindow != 0)
4460         wrefresh(rwindow);
4461
4462     move(0, 0);
4463     clrtoeol();
4464     return (rwindow);
4465 }
4466
4467 static void
4468 newwin_move(FRAME * curp, int dy, int dx)
4469 {
4470     WINDOW *win = frame_win(curp);
4471     int cur_y, cur_x;
4472     int max_y, max_x;
4473
4474     getyx(win, cur_y, cur_x);
4475     getmaxyx(win, max_y, max_x);
4476     if ((cur_x += dx) < 0)
4477         cur_x = 0;
4478     else if (cur_x >= max_x)
4479         cur_x = max_x - 1;
4480     if ((cur_y += dy) < 0)
4481         cur_y = 0;
4482     else if (cur_y >= max_y)
4483         cur_y = max_y - 1;
4484     wmove(win, cur_y, cur_x);
4485 }
4486
4487 static FRAME *
4488 delete_framed(FRAME * fp, bool showit)
4489 {
4490     FRAME *np = 0;