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