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