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