]> ncurses.scripts.mit.edu Git - ncurses.git/blob - test/ncurses.c
ncurses 6.0 - patch 20170715
[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.453 2017/06/24 20:49:44 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 #undef max_colors
151 static int max_colors;          /* the actual number of colors we'll use */
152 static int min_colors;          /* the minimum color code */
153 static bool use_colors;         /* true if we use colors */
154
155 #undef max_pairs
156 static int max_pairs;           /* ...and the number of color pairs */
157
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
166 static void main_menu(bool);
167 static void failed(const char *s) GCC_NORETURN;
168
169 static void
170 failed(const char *s)
171 {
172     perror(s);
173     endwin();
174     ExitProgram(EXIT_FAILURE);
175 }
176
177 /* The behavior of mvhline, mvvline for negative/zero length is unspecified,
178  * though we can rely on negative x/y values to stop the macro.
179  */
180 static void
181 do_h_line(int y, int x, chtype c, int to)
182 {
183     if ((to) > (x))
184         MvHLine(y, x, c, (to) - (x));
185 }
186
187 static void
188 do_v_line(int y, int x, chtype c, int to)
189 {
190     if ((to) > (y))
191         MvVLine(y, x, c, (to) - (y));
192 }
193
194 static void
195 Repaint(void)
196 {
197     touchwin(stdscr);
198     touchwin(curscr);
199     wrefresh(curscr);
200 }
201
202 static bool
203 isQuit(int c, bool escape)
204 {
205     return ((c) == QUIT || (escape && ((c) == ESCAPE)));
206 }
207 #define case_QUIT       QUIT: case ESCAPE
208
209 /* Common function to allow ^T to toggle trace-mode in the middle of a test
210  * so that trace-files can be made smaller.
211  */
212 static int
213 wGetchar(WINDOW *win)
214 {
215     int c;
216 #ifdef TRACE
217     while ((c = wgetch(win)) == CTRL('T')) {
218         if (_nc_tracing) {
219             save_trace = _nc_tracing;
220             Trace(("TOGGLE-TRACING OFF"));
221             _nc_tracing = 0;
222         } else {
223             _nc_tracing = save_trace;
224         }
225         trace(_nc_tracing);
226         if (_nc_tracing)
227             Trace(("TOGGLE-TRACING ON"));
228     }
229 #else
230     c = wgetch(win);
231 #endif
232     return c;
233 }
234 #define Getchar() wGetchar(stdscr)
235
236 /* replaces wgetnstr(), since we want to be able to edit values */
237 static void
238 wGetstring(WINDOW *win, char *buffer, int limit)
239 {
240     int y0, x0, x, ch;
241     bool done = FALSE;
242
243     echo();
244     getyx(win, y0, x0);
245     (void) wattrset(win, A_REVERSE);
246
247     x = (int) strlen(buffer);
248     while (!done) {
249         if (x > (int) strlen(buffer))
250             x = (int) strlen(buffer);
251         wmove(win, y0, x0);
252         wprintw(win, "%-*s", limit, buffer);
253         wmove(win, y0, x0 + x);
254         switch (ch = wGetchar(win)) {
255         case '\n':
256         case KEY_ENTER:
257             done = TRUE;
258             break;
259         case CTRL('U'):
260             *buffer = '\0';
261             break;
262         case '\b':
263         case KEY_BACKSPACE:
264         case KEY_DC:
265             if (x > 0) {
266                 int j;
267                 for (j = --x; (buffer[j] = buffer[j + 1]) != '\0'; ++j) {
268                     ;
269                 }
270             } else {
271                 beep();
272             }
273             break;
274         case KEY_LEFT:
275             if (x > 0) {
276                 --x;
277             } else {
278                 flash();
279             }
280             break;
281         case KEY_RIGHT:
282             ++x;
283             break;
284         default:
285             if (!isprint(ch) || ch >= KEY_MIN) {
286                 beep();
287             } else if ((int) strlen(buffer) < limit) {
288                 int j;
289                 for (j = (int) strlen(buffer) + 1; j > x; --j) {
290                     buffer[j] = buffer[j - 1];
291                 }
292                 buffer[x++] = (char) ch;
293             } else {
294                 flash();
295             }
296         }
297     }
298
299     wattroff(win, A_REVERSE);
300     wmove(win, y0, x0);
301     noecho();
302 }
303
304 #if USE_WIDEC_SUPPORT
305 static wchar_t
306 fullwidth_digit(int ch)
307 {
308     return (wchar_t) (ch + 0xff10 - '0');
309 }
310
311 static void
312 make_fullwidth_text(wchar_t *target, const char *source)
313 {
314     int ch;
315     while ((ch = *source++) != 0) {
316         *target++ = fullwidth_digit(ch);
317     }
318     *target = 0;
319 }
320
321 static void
322 make_narrow_text(wchar_t *target, const char *source)
323 {
324     int ch;
325     while ((ch = *source++) != 0) {
326         *target++ = (wchar_t) ch;
327     }
328     *target = 0;
329 }
330
331 #if USE_LIBPANEL
332 static void
333 make_fullwidth_digit(cchar_t *target, int digit)
334 {
335     wchar_t source[2];
336
337     source[0] = fullwidth_digit(digit + '0');
338     source[1] = 0;
339     setcchar(target, source, A_NORMAL, 0, 0);
340 }
341 #endif
342
343 static int
344 wGet_wchar(WINDOW *win, wint_t *result)
345 {
346     int c;
347 #ifdef TRACE
348     while ((c = wget_wch(win, result)) == CTRL('T')) {
349         if (_nc_tracing) {
350             save_trace = _nc_tracing;
351             Trace(("TOGGLE-TRACING OFF"));
352             _nc_tracing = 0;
353         } else {
354             _nc_tracing = save_trace;
355         }
356         trace(_nc_tracing);
357         if (_nc_tracing)
358             Trace(("TOGGLE-TRACING ON"));
359     }
360 #else
361     c = wget_wch(win, result);
362 #endif
363     return c;
364 }
365 #define Get_wchar(result) wGet_wchar(stdscr, result)
366
367 /* replaces wgetn_wstr(), since we want to be able to edit values */
368 static void
369 wGet_wstring(WINDOW *win, wchar_t *buffer, int limit)
370 {
371     int y0, x0, x;
372     wint_t ch;
373     bool done = FALSE;
374     bool fkey = FALSE;
375
376     echo();
377     getyx(win, y0, x0);
378     (void) wattrset(win, A_REVERSE);
379
380     x = (int) wcslen(buffer);
381     while (!done) {
382         if (x > (int) wcslen(buffer))
383             x = (int) wcslen(buffer);
384
385         /* clear the "window' */
386         wmove(win, y0, x0);
387         wprintw(win, "%*s", limit, " ");
388
389         /* write the existing buffer contents */
390         wmove(win, y0, x0);
391         waddnwstr(win, buffer, limit);
392
393         /* positions the cursor past character 'x' */
394         wmove(win, y0, x0);
395         waddnwstr(win, buffer, x);
396
397         switch (wGet_wchar(win, &ch)) {
398         case KEY_CODE_YES:
399             fkey = TRUE;
400             switch (ch) {
401             case KEY_ENTER:
402                 ch = '\n';
403                 fkey = FALSE;
404                 break;
405             case KEY_BACKSPACE:
406             case KEY_DC:
407                 ch = '\b';
408                 fkey = FALSE;
409                 break;
410             case KEY_LEFT:
411             case KEY_RIGHT:
412                 break;
413             default:
414                 ch = (wint_t) -1;
415                 break;
416             }
417             break;
418         case OK:
419             fkey = FALSE;
420             break;
421         default:
422             ch = (wint_t) -1;
423             fkey = TRUE;
424             break;
425         }
426
427         switch (ch) {
428         case '\n':
429             done = TRUE;
430             break;
431         case CTRL('U'):
432             *buffer = '\0';
433             break;
434         case '\b':
435             if (x > 0) {
436                 int j;
437                 for (j = --x; (buffer[j] = buffer[j + 1]) != '\0'; ++j) {
438                     ;
439                 }
440             } else {
441                 beep();
442             }
443             break;
444         case KEY_LEFT:
445             if (x > 0) {
446                 --x;
447             } else {
448                 beep();
449             }
450             break;
451         case KEY_RIGHT:
452             ++x;
453             break;
454         default:
455             if (fkey) {
456                 beep();
457             } else if ((int) wcslen(buffer) < limit) {
458                 int j;
459                 for (j = (int) wcslen(buffer) + 1; j > x; --j) {
460                     buffer[j] = buffer[j - 1];
461                 }
462                 buffer[x++] = (wchar_t) ch;
463             } else {
464                 beep();
465             }
466         }
467     }
468
469     wattroff(win, A_REVERSE);
470     wmove(win, y0, x0);
471     noecho();
472 }
473
474 #endif
475
476 static void
477 Pause(void)
478 {
479     move(LINES - 1, 0);
480     addstr("Press any key to continue... ");
481     (void) Getchar();
482 }
483
484 static void
485 Cannot(const char *what)
486 {
487     printw("\nThis %s terminal %s\n\n", getenv("TERM"), what);
488     Pause();
489 }
490
491 static void
492 ShellOut(bool message)
493 {
494     if (message)
495         addstr("Shelling out...");
496     def_prog_mode();
497     endwin();
498 #ifdef __MINGW32__
499     system("cmd.exe");
500 #else
501     IGNORE_RC(system("sh"));
502 #endif
503     if (message)
504         addstr("returned from shellout.\n");
505     refresh();
506 }
507
508 #ifdef NCURSES_MOUSE_VERSION
509 /*
510  * This function is the same as _tracemouse(), but we cannot count on that
511  * being available in the non-debug library.
512  */
513 static const char *
514 mouse_decode(MEVENT const *ep)
515 {
516     static char buf[80 + (5 * 10) + (32 * 15)];
517
518     (void) _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf))
519                        "id %2d at (%2d, %2d, %d) state %4lx = {",
520                        ep->id, ep->x, ep->y, ep->z, (unsigned long) ep->bstate);
521
522 #define SHOW(m, s) \
523         if ((ep->bstate & m)==m) { \
524                 _nc_STRCAT(buf, s, sizeof(buf)); \
525                 _nc_STRCAT(buf, ", ", sizeof(buf)); \
526         }
527
528     SHOW(BUTTON1_RELEASED, "release-1");
529     SHOW(BUTTON1_PRESSED, "press-1");
530     SHOW(BUTTON1_CLICKED, "click-1");
531     SHOW(BUTTON1_DOUBLE_CLICKED, "doubleclick-1");
532     SHOW(BUTTON1_TRIPLE_CLICKED, "tripleclick-1");
533 #if NCURSES_MOUSE_VERSION == 1
534     SHOW(BUTTON1_RESERVED_EVENT, "reserved-1");
535 #endif
536
537     SHOW(BUTTON2_RELEASED, "release-2");
538     SHOW(BUTTON2_PRESSED, "press-2");
539     SHOW(BUTTON2_CLICKED, "click-2");
540     SHOW(BUTTON2_DOUBLE_CLICKED, "doubleclick-2");
541     SHOW(BUTTON2_TRIPLE_CLICKED, "tripleclick-2");
542 #if NCURSES_MOUSE_VERSION == 1
543     SHOW(BUTTON2_RESERVED_EVENT, "reserved-2");
544 #endif
545
546     SHOW(BUTTON3_RELEASED, "release-3");
547     SHOW(BUTTON3_PRESSED, "press-3");
548     SHOW(BUTTON3_CLICKED, "click-3");
549     SHOW(BUTTON3_DOUBLE_CLICKED, "doubleclick-3");
550     SHOW(BUTTON3_TRIPLE_CLICKED, "tripleclick-3");
551 #if NCURSES_MOUSE_VERSION == 1
552     SHOW(BUTTON3_RESERVED_EVENT, "reserved-3");
553 #endif
554
555     SHOW(BUTTON4_RELEASED, "release-4");
556     SHOW(BUTTON4_PRESSED, "press-4");
557     SHOW(BUTTON4_CLICKED, "click-4");
558     SHOW(BUTTON4_DOUBLE_CLICKED, "doubleclick-4");
559     SHOW(BUTTON4_TRIPLE_CLICKED, "tripleclick-4");
560 #if NCURSES_MOUSE_VERSION == 1
561     SHOW(BUTTON4_RESERVED_EVENT, "reserved-4");
562 #endif
563
564 #if NCURSES_MOUSE_VERSION == 2
565     SHOW(BUTTON5_RELEASED, "release-5");
566     SHOW(BUTTON5_PRESSED, "press-5");
567     SHOW(BUTTON5_CLICKED, "click-5");
568     SHOW(BUTTON5_DOUBLE_CLICKED, "doubleclick-5");
569     SHOW(BUTTON5_TRIPLE_CLICKED, "tripleclick-5");
570 #endif
571
572     SHOW(BUTTON_CTRL, "ctrl");
573     SHOW(BUTTON_SHIFT, "shift");
574     SHOW(BUTTON_ALT, "alt");
575     SHOW(ALL_MOUSE_EVENTS, "all-events");
576     SHOW(REPORT_MOUSE_POSITION, "position");
577
578 #undef SHOW
579
580     if (buf[strlen(buf) - 1] == ' ')
581         buf[strlen(buf) - 2] = '\0';
582     _nc_STRCAT(buf, "}", sizeof(buf));
583     return (buf);
584 }
585
586 static void
587 show_mouse(WINDOW *win)
588 {
589     int y, x;
590     MEVENT event;
591     bool outside;
592     bool show_loc;
593
594     getmouse(&event);
595     outside = !wenclose(win, event.y, event.x);
596
597     if (outside) {
598         (void) wstandout(win);
599         waddstr(win, "KEY_MOUSE");
600         (void) wstandend(win);
601     } else {
602         waddstr(win, "KEY_MOUSE");
603     }
604     wprintw(win, ", %s", mouse_decode(&event));
605
606     if (outside)
607         win = stdscr;
608
609     show_loc = wmouse_trafo(win, &event.y, &event.x, FALSE);
610
611     if (show_loc) {
612         getyx(win, y, x);
613         wmove(win, event.y, event.x);
614         waddch(win, '*');
615         wmove(win, y, x);
616     }
617
618     if (outside)
619         wnoutrefresh(win);
620 }
621 #endif /* NCURSES_MOUSE_VERSION */
622
623 /****************************************************************************
624  *
625  * Character input test
626  *
627  ****************************************************************************/
628
629 #define NUM_GETCH_FLAGS 256
630 typedef bool GetchFlags[NUM_GETCH_FLAGS];
631
632 static void
633 setup_getch(WINDOW *win, GetchFlags flags)
634 {
635     keypad(win, flags['k']);    /* should be redundant, but for testing */
636     meta(win, flags['m']);      /* force this to a known state */
637     if (flags['e'])
638         echo();
639     else
640         noecho();
641 }
642
643 static void
644 init_getch(WINDOW *win, GetchFlags flags, int delay)
645 {
646     memset(flags, FALSE, NUM_GETCH_FLAGS);
647     flags[UChar('k')] = (win == stdscr);
648     flags[UChar('m')] = TRUE;
649     flags[UChar('t')] = (delay != 0);
650
651     setup_getch(win, flags);
652 }
653
654 static bool
655 blocking_getch(GetchFlags flags, int delay)
656 {
657     return ((delay < 0) && flags['t']);
658 }
659
660 #define ExitOnEscape() (flags[UChar('k')] && flags[UChar('t')])
661
662 static void
663 wgetch_help(WINDOW *win, GetchFlags flags)
664 {
665     static const char *help[] =
666     {
667         "e  -- toggle echo mode"
668         ,"g  -- triggers a getstr test"
669         ,"k  -- toggle keypad/literal mode"
670         ,"m  -- toggle meta (7-bit/8-bit) mode"
671         ,"^q -- quit"
672         ,"s  -- shell out"
673         ,"t  -- toggle timeout"
674         ,"w  -- create a new window"
675 #ifdef SIGTSTP
676         ,"z  -- suspend this process"
677 #endif
678     };
679     int y, x;
680     unsigned chk = ((SIZEOF(help) + 1) / 2);
681     unsigned n;
682
683     getyx(win, y, x);
684     move(0, 0);
685     printw("Type any key to see its %s value.  Also:\n",
686            flags['k'] ? "keypad" : "literal");
687     for (n = 0; n < SIZEOF(help); ++n) {
688         const char *msg = help[n];
689         int row = 1 + (int) (n % chk);
690         int col = (n >= chk) ? COLS / 2 : 0;
691         int flg = ((strstr(msg, "toggle") != 0)
692                    && (flags[UChar(*msg)] != FALSE));
693         if (*msg == '^' && ExitOnEscape())
694             msg = "^[,^q -- quit";
695         if (flg)
696             (void) standout();
697         MvPrintw(row, col, "%s", msg);
698         if (col == 0)
699             clrtoeol();
700         if (flg)
701             (void) standend();
702     }
703     wrefresh(stdscr);
704     wmove(win, y, x);
705 }
706
707 static void
708 wgetch_wrap(WINDOW *win, int first_y)
709 {
710     int last_y = getmaxy(win) - 1;
711     int y = getcury(win) + 1;
712
713     if (y >= last_y)
714         y = first_y;
715     wmove(win, y, 0);
716     wclrtoeol(win);
717 }
718
719 #if defined(KEY_RESIZE) && HAVE_WRESIZE
720 typedef struct {
721     WINDOW *text;
722     WINDOW *frame;
723 } WINSTACK;
724
725 static WINSTACK *winstack = 0;
726 static unsigned len_winstack = 0;
727
728 static void
729 forget_boxes(void)
730 {
731     if (winstack != 0) {
732         free(winstack);
733     }
734     winstack = 0;
735     len_winstack = 0;
736 }
737
738 static void
739 remember_boxes(unsigned level, WINDOW *txt_win, WINDOW *box_win)
740 {
741     unsigned need = (level + 1) * 2;
742
743     assert(level < (unsigned) COLS);
744
745     if (winstack == 0) {
746         len_winstack = 20;
747         winstack = typeMalloc(WINSTACK, len_winstack);
748     } else if (need >= len_winstack) {
749         len_winstack = need;
750         winstack = typeRealloc(WINSTACK, len_winstack, winstack);
751     }
752     if (!winstack)
753         failed("remember_boxes");
754     winstack[level].text = txt_win;
755     winstack[level].frame = box_win;
756 }
757
758 #if USE_SOFTKEYS && (defined(NCURSES_VERSION_PATCH) && NCURSES_VERSION_PATCH < 20071229) && NCURSES_EXT_FUNCS
759 static void
760 slk_repaint(void)
761 {
762     /* this chunk is now done in resize_term() */
763     slk_touch();
764     slk_clear();
765     slk_noutrefresh();
766 }
767
768 #else
769 #define slk_repaint()           /* nothing */
770 #endif
771
772 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
773 /*
774  * For wgetch_test(), we create pairs of windows - one for a box, one for text.
775  * Resize both and paint the box in the parent.
776  */
777 static void
778 resize_boxes(unsigned level, WINDOW *win)
779 {
780     unsigned n;
781     int base = 5;
782     int high = LINES - base;
783     int wide = COLS;
784
785     touchwin(stdscr);
786     wnoutrefresh(stdscr);
787
788     slk_repaint();
789
790     for (n = 0; n < level; ++n) {
791         wresize(winstack[n].frame, high, wide);
792         wresize(winstack[n].text, high - 2, wide - 2);
793         high -= 2;
794         wide -= 2;
795         werase(winstack[n].text);
796         box(winstack[n].frame, 0, 0);
797         wnoutrefresh(winstack[n].frame);
798         wprintw(winstack[n].text,
799                 "size %dx%d\n",
800                 getmaxy(winstack[n].text),
801                 getmaxx(winstack[n].text));
802         wnoutrefresh(winstack[n].text);
803         if (winstack[n].text == win)
804             break;
805     }
806     doupdate();
807 }
808 #endif /* resize_boxes */
809 #else
810 #define forget_boxes()          /* nothing */
811 #define remember_boxes(level,text,frame)        /* nothing */
812 #endif
813
814 /*
815  * Return-code is OK/ERR or a keyname.
816  */
817 static const char *
818 ok_keyname(int code)
819 {
820     return ((code == OK) ? "OK" : ((code == ERR) ? "ERR" : keyname(code)));
821 }
822
823 static void
824 wgetch_test(unsigned level, WINDOW *win, int delay)
825 {
826     char buf[BUFSIZ];
827     int first_y, first_x;
828     int c;
829     int incount = 0;
830     GetchFlags flags;
831
832     init_getch(win, flags, delay);
833     notimeout(win, FALSE);
834     wtimeout(win, delay);
835     getyx(win, first_y, first_x);
836
837     wgetch_help(win, flags);
838     wsetscrreg(win, first_y, getmaxy(win) - 1);
839     scrollok(win, TRUE);
840
841     for (;;) {
842         while ((c = wGetchar(win)) == ERR) {
843             incount++;
844             if (blocking_getch(flags, delay)) {
845                 (void) wprintw(win, "%05d: input error", incount);
846                 break;
847             } else {
848                 (void) wprintw(win, "%05d: input timed out", incount);
849             }
850             wgetch_wrap(win, first_y);
851         }
852         if (c == ERR && blocking_getch(flags, delay)) {
853             wprintw(win, "ERR");
854             wgetch_wrap(win, first_y);
855         } else if (isQuit(c, ExitOnEscape())) {
856             break;
857         } else if (c == 'e') {
858             flags[UChar('e')] = !flags[UChar('e')];
859             setup_getch(win, flags);
860             wgetch_help(win, flags);
861         } else if (c == 'g') {
862             waddstr(win, "getstr test: ");
863             echo();
864             c = wgetnstr(win, buf, sizeof(buf) - 1);
865             noecho();
866             wprintw(win, "I saw %d characters:\n\t`%s' (%s).",
867                     (int) strlen(buf), buf,
868                     ok_keyname(c));
869             wclrtoeol(win);
870             wgetch_wrap(win, first_y);
871         } else if (c == 'k') {
872             flags[UChar('k')] = !flags[UChar('k')];
873             setup_getch(win, flags);
874             wgetch_help(win, flags);
875         } else if (c == 'm') {
876             flags[UChar('m')] = !flags[UChar('m')];
877             setup_getch(win, flags);
878             wgetch_help(win, flags);
879         } else if (c == 's') {
880             ShellOut(TRUE);
881         } else if (c == 't') {
882             notimeout(win, flags[UChar('t')]);
883             flags[UChar('t')] = !flags[UChar('t')];
884             wgetch_help(win, flags);
885         } else if (c == 'w') {
886             int high = getmaxy(win) - 1 - first_y + 1;
887             int wide = getmaxx(win) - first_x;
888             int old_y, old_x;
889             int new_y = first_y + getbegy(win);
890             int new_x = first_x + getbegx(win);
891
892             getyx(win, old_y, old_x);
893             if (high > 2 && wide > 2) {
894                 WINDOW *wb = newwin(high, wide, new_y, new_x);
895                 WINDOW *wi = newwin(high - 2, wide - 2, new_y + 1, new_x + 1);
896
897                 box(wb, 0, 0);
898                 wrefresh(wb);
899                 wmove(wi, 0, 0);
900                 remember_boxes(level, wi, wb);
901                 wgetch_test(level + 1, wi, delay);
902                 delwin(wi);
903                 delwin(wb);
904
905                 wgetch_help(win, flags);
906                 wmove(win, old_y, old_x);
907                 touchwin(win);
908                 wrefresh(win);
909                 doupdate();
910             }
911 #ifdef SIGTSTP
912         } else if (c == 'z') {
913             kill(getpid(), SIGTSTP);
914 #endif
915         } else {
916             wprintw(win, "Key pressed: %04o ", c);
917 #ifdef NCURSES_MOUSE_VERSION
918             if (c == KEY_MOUSE) {
919                 show_mouse(win);
920             } else
921 #endif /* NCURSES_MOUSE_VERSION */
922             if (c >= KEY_MIN) {
923 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
924                 if (c == KEY_RESIZE) {
925                     resize_boxes(level, win);
926                 }
927 #endif
928                 (void) waddstr(win, keyname(c));
929             } else if (c >= 0x80) {
930                 unsigned c2 = (unsigned) c;
931 #if !(defined(NCURSES_VERSION) || defined(_XOPEN_CURSES))
932                 /* at least Solaris SVR4 curses breaks unctrl(128), etc. */
933                 c2 &= 0x7f;
934 #endif
935                 if (isprint(c))
936                     (void) wprintw(win, "%c", UChar(c));
937                 else if (c2 != UChar(c))
938                     (void) wprintw(win, "M-%s", unctrl(c2));
939                 else
940                     (void) wprintw(win, "%s", unctrl(c2));
941                 waddstr(win, " (high-half character)");
942             } else {
943                 if (isprint(c))
944                     (void) wprintw(win, "%c (ASCII printable character)", c);
945                 else
946                     (void) wprintw(win, "%s (ASCII control character)",
947                                    unctrl(UChar(c)));
948             }
949             wgetch_wrap(win, first_y);
950         }
951     }
952
953     wtimeout(win, -1);
954
955     if (!level)
956         init_getch(win, flags, delay);
957 }
958
959 static int
960 begin_getch_test(void)
961 {
962     char buf[BUFSIZ];
963     int delay;
964
965     refresh();
966
967 #ifdef NCURSES_MOUSE_VERSION
968     mousemask(ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION, (mmask_t *) 0);
969 #endif
970
971     (void) printw("Delay in 10ths of a second (<CR> for blocking input)? ");
972     echo();
973     getnstr(buf, sizeof(buf) - 1);
974     noecho();
975     nonl();
976
977     if (isdigit(UChar(buf[0]))) {
978         delay = atoi(buf) * 100;
979     } else {
980         delay = -1;
981     }
982     raw();
983     move(6, 0);
984     return delay;
985 }
986
987 static void
988 finish_getch_test(void)
989 {
990 #ifdef NCURSES_MOUSE_VERSION
991     mousemask(0, (mmask_t *) 0);
992 #endif
993     erase();
994     noraw();
995     nl();
996     endwin();
997 }
998
999 static void
1000 getch_test(void)
1001 {
1002     int delay = begin_getch_test();
1003
1004     slk_restore();
1005     wgetch_test(0, stdscr, delay);
1006     forget_boxes();
1007     finish_getch_test();
1008     slk_clear();
1009 }
1010
1011 #if USE_WIDEC_SUPPORT
1012 /*
1013  * For wget_wch_test(), we create pairs of windows - one for a box, one for text.
1014  * Resize both and paint the box in the parent.
1015  */
1016 #if defined(KEY_RESIZE) && HAVE_WRESIZE
1017 static void
1018 resize_wide_boxes(unsigned level, WINDOW *win)
1019 {
1020     unsigned n;
1021     int base = 5;
1022     int high = LINES - base;
1023     int wide = COLS;
1024
1025     touchwin(stdscr);
1026     wnoutrefresh(stdscr);
1027
1028     slk_repaint();
1029
1030     for (n = 0; n < level; ++n) {
1031         wresize(winstack[n].frame, high, wide);
1032         wresize(winstack[n].text, high - 2, wide - 2);
1033         high -= 2;
1034         wide -= 2;
1035         werase(winstack[n].text);
1036         box_set(winstack[n].frame, 0, 0);
1037         wnoutrefresh(winstack[n].frame);
1038         wprintw(winstack[n].text,
1039                 "size %dx%d\n",
1040                 getmaxy(winstack[n].text),
1041                 getmaxx(winstack[n].text));
1042         wnoutrefresh(winstack[n].text);
1043         if (winstack[n].text == win)
1044             break;
1045     }
1046     doupdate();
1047 }
1048 #endif /* KEY_RESIZE */
1049
1050 static char *
1051 wcstos(const wchar_t *src)
1052 {
1053     int need;
1054     char *result = 0;
1055     const wchar_t *tmp = src;
1056 #ifndef state_unused
1057     mbstate_t state;
1058 #endif
1059
1060     reset_wchars(state);
1061     if ((need = (int) count_wchars(tmp, 0, &state)) > 0) {
1062         unsigned have = (unsigned) need;
1063         if ((result = typeCalloc(char, have + 1)) != 0) {
1064             tmp = src;
1065             if (trans_wchars(result, tmp, have, &state) != have) {
1066                 free(result);
1067                 result = 0;
1068             }
1069         } else {
1070             failed("wcstos");
1071         }
1072     }
1073     return result;
1074 }
1075
1076 static void
1077 wget_wch_test(unsigned level, WINDOW *win, int delay)
1078 {
1079     wchar_t wchar_buf[BUFSIZ];
1080     wint_t wint_buf[BUFSIZ];
1081     int first_y, first_x;
1082     wint_t c;
1083     int incount = 0;
1084     GetchFlags flags;
1085     int code;
1086     char *temp;
1087
1088     init_getch(win, flags, delay);
1089     notimeout(win, FALSE);
1090     wtimeout(win, delay);
1091     getyx(win, first_y, first_x);
1092
1093     wgetch_help(win, flags);
1094     wsetscrreg(win, first_y, getmaxy(win) - 1);
1095     scrollok(win, TRUE);
1096
1097     for (;;) {
1098         while ((code = wGet_wchar(win, &c)) == ERR) {
1099             incount++;
1100             if (blocking_getch(flags, delay)) {
1101                 (void) wprintw(win, "%05d: input error", incount);
1102                 break;
1103             } else {
1104                 (void) wprintw(win, "%05d: input timed out", incount);
1105             }
1106             wgetch_wrap(win, first_y);
1107         }
1108         if (code == ERR && blocking_getch(flags, delay)) {
1109             wprintw(win, "ERR");
1110             wgetch_wrap(win, first_y);
1111         } else if (isQuit((int) c, ExitOnEscape())) {
1112             break;
1113         } else if (c == 'e') {
1114             flags[UChar('e')] = !flags[UChar('e')];
1115             setup_getch(win, flags);
1116             wgetch_help(win, flags);
1117         } else if (c == 'g') {
1118             waddstr(win, "getstr test: ");
1119             echo();
1120             code = wgetn_wstr(win, wint_buf, BUFSIZ - 1);
1121             noecho();
1122             if (code == ERR) {
1123                 wprintw(win, "wgetn_wstr returns an error.");
1124             } else {
1125                 int n;
1126                 for (n = 0; (wchar_buf[n] = (wchar_t) wint_buf[n]) != 0; ++n) {
1127                     ;
1128                 }
1129                 if ((temp = wcstos(wchar_buf)) != 0) {
1130                     wprintw(win, "I saw %d characters:\n\t`%s'.",
1131                             (int) wcslen(wchar_buf), temp);
1132                     free(temp);
1133                 } else {
1134                     wprintw(win, "I saw %d characters (cannot convert).",
1135                             (int) wcslen(wchar_buf));
1136                 }
1137             }
1138             wclrtoeol(win);
1139             wgetch_wrap(win, first_y);
1140         } else if (c == 'k') {
1141             flags[UChar('k')] = !flags[UChar('k')];
1142             setup_getch(win, flags);
1143             wgetch_help(win, flags);
1144         } else if (c == 'm') {
1145             flags[UChar('m')] = !flags[UChar('m')];
1146             setup_getch(win, flags);
1147             wgetch_help(win, flags);
1148         } else if (c == 's') {
1149             ShellOut(TRUE);
1150         } else if (c == 't') {
1151             notimeout(win, flags[UChar('t')]);
1152             flags[UChar('t')] = !flags[UChar('t')];
1153             wgetch_help(win, flags);
1154         } else if (c == 'w') {
1155             int high = getmaxy(win) - 1 - first_y + 1;
1156             int wide = getmaxx(win) - first_x;
1157             int old_y, old_x;
1158             int new_y = first_y + getbegy(win);
1159             int new_x = first_x + getbegx(win);
1160
1161             getyx(win, old_y, old_x);
1162             if (high > 2 && wide > 2) {
1163                 WINDOW *wb = newwin(high, wide, new_y, new_x);
1164                 WINDOW *wi = newwin(high - 2, wide - 2, new_y + 1, new_x + 1);
1165
1166                 box_set(wb, 0, 0);
1167                 wrefresh(wb);
1168                 wmove(wi, 0, 0);
1169                 remember_boxes(level, wi, wb);
1170                 wget_wch_test(level + 1, wi, delay);
1171                 delwin(wi);
1172                 delwin(wb);
1173
1174                 wgetch_help(win, flags);
1175                 wmove(win, old_y, old_x);
1176                 touchwin(win);
1177                 wrefresh(win);
1178             }
1179 #ifdef SIGTSTP
1180         } else if (c == 'z') {
1181             kill(getpid(), SIGTSTP);
1182 #endif
1183         } else {
1184             wprintw(win, "Key pressed: %04o ", (int) c);
1185 #ifdef NCURSES_MOUSE_VERSION
1186             if (c == KEY_MOUSE) {
1187                 show_mouse(win);
1188             } else
1189 #endif /* NCURSES_MOUSE_VERSION */
1190             if (code == KEY_CODE_YES) {
1191 #if defined(KEY_RESIZE) && HAVE_WRESIZE
1192                 if (c == KEY_RESIZE) {
1193                     resize_wide_boxes(level, win);
1194                 }
1195 #endif
1196                 (void) waddstr(win, keyname((wchar_t) c));
1197             } else {
1198                 (void) waddstr(win, key_name((wchar_t) c));
1199                 if (c < 256 && iscntrl(c)) {
1200                     (void) wprintw(win, " (control character)");
1201                 } else {
1202                     (void) wprintw(win, " = %#x (printable character)",
1203                                    (unsigned) c);
1204                 }
1205             }
1206             wgetch_wrap(win, first_y);
1207         }
1208     }
1209
1210     wtimeout(win, -1);
1211
1212     if (!level)
1213         init_getch(win, flags, delay);
1214 }
1215
1216 static void
1217 get_wch_test(void)
1218 {
1219     int delay = begin_getch_test();
1220
1221     slk_restore();
1222     wget_wch_test(0, stdscr, delay);
1223     forget_boxes();
1224     finish_getch_test();
1225     slk_clear();
1226 }
1227 #endif
1228
1229 /****************************************************************************
1230  *
1231  * Character attributes test
1232  *
1233  ****************************************************************************/
1234
1235 #if HAVE_SETUPTERM || HAVE_TGETENT
1236 #define get_ncv() TIGETNUM("ncv","NC")
1237 #define get_xmc() TIGETNUM("xmc","sg")
1238 #else
1239 #define get_ncv() -1
1240 #define get_xmc() -1
1241 #endif
1242
1243 #if !HAVE_TERMATTRS
1244 static chtype
1245 my_termattrs(void)
1246 {
1247     static int first = TRUE;
1248     static chtype result = 0;
1249
1250     if (first) {
1251 #if !HAVE_TIGETSTR
1252         char buffer[4096];
1253         char parsed[4096];
1254         char *area_pointer = parsed;
1255
1256         tgetent(buffer, getenv("TERM"));
1257 #endif
1258
1259         if (TIGETSTR("smso", "so"))
1260             result |= A_STANDOUT;
1261         if (TIGETSTR("smul", "us"))
1262             result |= A_UNDERLINE;
1263         if (TIGETSTR("rev", "mr"))
1264             result |= A_REVERSE;
1265         if (TIGETSTR("blink", "mb"))
1266             result |= A_BLINK;
1267         if (TIGETSTR("dim", "mh"))
1268             result |= A_DIM;
1269         if (TIGETSTR("bold", "md"))
1270             result |= A_BOLD;
1271         if (TIGETSTR("smacs", "ac"))
1272             result |= A_ALTCHARSET;
1273
1274         first = FALSE;
1275     }
1276     return result;
1277 }
1278 #define termattrs() my_termattrs()
1279 #endif
1280
1281 #define ATTRSTRING_1ST 32       /* ' ' */
1282 #define ATTRSTRING_END 126      /* '~' */
1283
1284 #define COLS_PRE_ATTRS 5
1285 #define COLS_AFT_ATTRS 15
1286 #define COL_ATTRSTRING (COLS_PRE_ATTRS + 17)
1287 #define LEN_ATTRSTRING (COLS - (COL_ATTRSTRING + COLS_AFT_ATTRS))
1288 #define MAX_ATTRSTRING (ATTRSTRING_END + 1 - ATTRSTRING_1ST)
1289
1290 static char attr_test_string[MAX_ATTRSTRING + 1];
1291
1292 static void
1293 attr_legend(WINDOW *helpwin)
1294 {
1295     int row = 1;
1296     int col = 1;
1297
1298     MvWPrintw(helpwin, row++, col,
1299               "ESC to exit.");
1300     MvWPrintw(helpwin, row++, col,
1301               "^L repaints.");
1302     ++row;
1303     MvWPrintw(helpwin, row++, col,
1304               "Modify the test strings:");
1305     MvWPrintw(helpwin, row++, col,
1306               "  A digit sets gaps on each side of displayed attributes");
1307     MvWPrintw(helpwin, row++, col,
1308               "  </> shifts the text left/right. ");
1309     ++row;
1310     MvWPrintw(helpwin, row++, col,
1311               "Toggles:");
1312     if (use_colors) {
1313         MvWPrintw(helpwin, row++, col,
1314                   "  f/F/b/B toggle foreground/background background color");
1315         MvWPrintw(helpwin, row++, col,
1316                   "  t/T     toggle text/background color attribute");
1317     }
1318     MvWPrintw(helpwin, row++, col,
1319               "  a/A     toggle ACS (alternate character set) mapping");
1320     MvWPrintw(helpwin, row, col,
1321               "  v/V     toggle video attribute to combine with each line");
1322 #if USE_WIDEC_SUPPORT
1323     MvWPrintw(helpwin, row, col,
1324               "  w/W     toggle normal/wide (double-width) test-characters");
1325 #endif
1326 }
1327
1328 static void
1329 show_color_attr(int fg, int bg, int tx)
1330 {
1331     if (use_colors) {
1332         printw("  Colors (fg %d, bg %d", fg, bg);
1333         if (tx >= 0)
1334             printw(", text %d", tx);
1335         printw("),");
1336     }
1337 }
1338
1339 static bool
1340 cycle_color_attr(int ch, NCURSES_COLOR_T *fg, NCURSES_COLOR_T *bg, NCURSES_COLOR_T *tx)
1341 {
1342     bool error = FALSE;
1343
1344     if (use_colors) {
1345         switch (ch) {
1346         case 'f':
1347             *fg = (NCURSES_COLOR_T) (*fg + 1);
1348             break;
1349         case 'F':
1350             *fg = (NCURSES_COLOR_T) (*fg - 1);
1351             break;
1352         case 'b':
1353             *bg = (NCURSES_COLOR_T) (*bg + 1);
1354             break;
1355         case 'B':
1356             *bg = (NCURSES_COLOR_T) (*bg - 1);
1357             break;
1358         case 't':
1359             *tx = (NCURSES_COLOR_T) (*tx + 1);
1360             break;
1361         case 'T':
1362             *tx = (NCURSES_COLOR_T) (*tx - 1);
1363             break;
1364         default:
1365             beep();
1366             error = TRUE;
1367             break;
1368         }
1369         if (*fg >= COLORS)
1370             *fg = (NCURSES_COLOR_T) min_colors;
1371         if (*fg < min_colors)
1372             *fg = (NCURSES_COLOR_T) (COLORS - 1);
1373         if (*bg >= COLORS)
1374             *bg = (NCURSES_COLOR_T) min_colors;
1375         if (*bg < min_colors)
1376             *bg = (NCURSES_COLOR_T) (COLORS - 1);
1377         if (*tx >= COLORS)
1378             *tx = -1;
1379         if (*tx < -1)
1380             *tx = (NCURSES_COLOR_T) (COLORS - 1);
1381     } else {
1382         beep();
1383         error = TRUE;
1384     }
1385     return error;
1386 }
1387
1388 static void
1389 adjust_attr_string(int adjust)
1390 {
1391     char save = attr_test_string[0];
1392     int first = ((int) UChar(save)) + adjust;
1393     int j, k;
1394
1395     if (first >= ATTRSTRING_1ST) {
1396         for (j = 0, k = first; j < MAX_ATTRSTRING; ++j, ++k) {
1397             if (k > ATTRSTRING_END)
1398                 break;
1399             attr_test_string[j] = (char) k;
1400             if (((k + 1 - first) % 5) == 0) {
1401                 if (++j >= MAX_ATTRSTRING)
1402                     break;
1403                 attr_test_string[j] = ' ';
1404             }
1405         }
1406         if ((LEN_ATTRSTRING - j) > 5) {
1407             attr_test_string[0] = save;
1408             adjust_attr_string(adjust - 1);
1409         } else {
1410             while (j < MAX_ATTRSTRING)
1411                 attr_test_string[j++] = ' ';
1412             attr_test_string[j] = '\0';
1413         }
1414     }
1415 }
1416
1417 /*
1418  * Prefer the right-end of the string for starting, since that maps to the
1419  * VT100 line-drawing.
1420  */
1421 static int
1422 default_attr_string(void)
1423 {
1424     int result = (ATTRSTRING_END - LEN_ATTRSTRING);
1425     result += (LEN_ATTRSTRING / 5);
1426     if (result < ATTRSTRING_1ST)
1427         result = ATTRSTRING_1ST;
1428     return result;
1429 }
1430
1431 static void
1432 init_attr_string(void)
1433 {
1434     attr_test_string[0] = (char) default_attr_string();
1435     adjust_attr_string(0);
1436 }
1437
1438 static int
1439 show_attr(WINDOW *win, int row, int skip, bool arrow, chtype attr, const char *name)
1440 {
1441     int ncv = get_ncv();
1442     chtype test = attr & (chtype) (~(A_ALTCHARSET | A_CHARTEXT));
1443
1444     if (arrow)
1445         MvPrintw(row, COLS_PRE_ATTRS - 3, "-->");
1446     MvPrintw(row, COLS_PRE_ATTRS, "%s mode:", name);
1447     MvPrintw(row, COL_ATTRSTRING - 1, "|");
1448     if (skip)
1449         printw("%*s", skip, " ");
1450     /*
1451      * Just for testing, write text using the alternate character set one
1452      * character at a time (to pass its rendition directly), and use the
1453      * string operation for the other attributes.
1454      */
1455     wmove(win, 0, 0);
1456     werase(win);
1457     if (attr & A_ALTCHARSET) {
1458         const char *s;
1459         chtype ch;
1460
1461         for (s = attr_test_string; *s != '\0'; ++s) {
1462             ch = UChar(*s);
1463             (void) waddch(win, ch | attr);
1464         }
1465     } else {
1466         (void) wattrset(win, AttrArg(attr, 0));
1467         (void) waddstr(win, attr_test_string);
1468         (void) wattroff(win, (int) attr);
1469     }
1470     if (skip)
1471         printw("%*s", skip, " ");
1472     MvPrintw(row, COL_ATTRSTRING + LEN_ATTRSTRING, "|");
1473     if (test != A_NORMAL) {
1474         if (!(termattrs() & test)) {
1475             printw(" (N/A)");
1476         } else {
1477             if (ncv > 0 && stdscr && (getbkgd(stdscr) & A_COLOR)) {
1478                 static const chtype table[] =
1479                 {
1480                     A_STANDOUT,
1481                     A_UNDERLINE,
1482                     A_REVERSE,
1483                     A_BLINK,
1484                     A_DIM,
1485                     A_BOLD,
1486 #ifdef A_INVIS
1487                     A_INVIS,
1488 #endif
1489 #ifdef A_ITALIC
1490                     A_ITALIC,
1491 #endif
1492                     A_PROTECT,
1493                     A_ALTCHARSET
1494                 };
1495                 unsigned n;
1496                 bool found = FALSE;
1497                 for (n = 0; n < SIZEOF(table); n++) {
1498                     if ((table[n] & attr) != 0
1499                         && ((1 << n) & ncv) != 0) {
1500                         found = TRUE;
1501                         break;
1502                     }
1503                 }
1504                 if (found)
1505                     printw(" (NCV)");
1506             }
1507             if ((termattrs() & test) != test) {
1508                 printw(" (Part)");
1509             }
1510         }
1511     }
1512     return row + 2;
1513 }
1514
1515 typedef struct {
1516     attr_t attr;
1517     NCURSES_CONST char *name;
1518 } ATTR_TBL;
1519 /* *INDENT-OFF* */
1520 static const ATTR_TBL attrs_to_test[] = {
1521     { A_STANDOUT,       "STANDOUT" },
1522     { A_REVERSE,        "REVERSE" },
1523     { A_BOLD,           "BOLD" },
1524     { A_UNDERLINE,      "UNDERLINE" },
1525     { A_DIM,            "DIM" },
1526     { A_BLINK,          "BLINK" },
1527     { A_PROTECT,        "PROTECT" },
1528 #ifdef A_INVIS
1529     { A_INVIS,          "INVISIBLE" },
1530 #endif
1531 #ifdef A_ITALIC
1532     { A_ITALIC,         "ITALIC" },
1533 #endif
1534     { A_NORMAL,         "NORMAL" },
1535 };
1536 /* *INDENT-ON* */
1537
1538 static unsigned
1539 init_attr_list(ATTR_TBL * target, attr_t attrs)
1540 {
1541     unsigned result = 0;
1542     size_t n;
1543
1544     for (n = 0; n < SIZEOF(attrs_to_test); ++n) {
1545         attr_t test = attrs_to_test[n].attr;
1546         if (test == A_NORMAL || (test & attrs) != 0) {
1547             target[result++] = attrs_to_test[n];
1548         }
1549     }
1550     return result;
1551 }
1552
1553 static bool
1554 attr_getc(int *skip,
1555           NCURSES_COLOR_T *fg,
1556           NCURSES_COLOR_T *bg,
1557           NCURSES_COLOR_T *tx,
1558           int *ac,
1559           unsigned *kc,
1560           unsigned limit)
1561 {
1562     bool result = TRUE;
1563     bool error = FALSE;
1564     WINDOW *helpwin;
1565
1566     do {
1567         int ch = Getchar();
1568
1569         error = FALSE;
1570         if (ch < 256 && isdigit(ch)) {
1571             *skip = (ch - '0');
1572         } else {
1573             switch (ch) {
1574             case CTRL('L'):
1575                 Repaint();
1576                 break;
1577             case HELP_KEY_1:
1578                 if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
1579                     box(helpwin, 0, 0);
1580                     attr_legend(helpwin);
1581                     wGetchar(helpwin);
1582                     delwin(helpwin);
1583                 }
1584                 break;
1585             case 'a':
1586                 *ac = 0;
1587                 break;
1588             case 'A':
1589                 *ac = A_ALTCHARSET;
1590                 break;
1591             case 'v':
1592                 if (*kc == 0)
1593                     *kc = limit - 1;
1594                 else
1595                     *kc -= 1;
1596                 break;
1597             case 'V':
1598                 *kc += 1;
1599                 if (*kc >= limit)
1600                     *kc = 0;
1601                 break;
1602             case '<':
1603                 adjust_attr_string(-1);
1604                 break;
1605             case '>':
1606                 adjust_attr_string(1);
1607                 break;
1608             case case_QUIT:
1609                 result = FALSE;
1610                 break;
1611             default:
1612                 error = cycle_color_attr(ch, fg, bg, tx);
1613                 break;
1614             }
1615         }
1616     } while (error);
1617     return result;
1618 }
1619
1620 static void
1621 attr_test(void)
1622 /* test text attributes */
1623 {
1624     int n;
1625     int skip = get_xmc();
1626     NCURSES_COLOR_T fg = COLOR_BLACK;   /* color pair 0 is special */
1627     NCURSES_COLOR_T bg = COLOR_BLACK;
1628     NCURSES_COLOR_T tx = -1;
1629     int ac = 0;
1630     unsigned j, k;
1631     WINDOW *my_wins[SIZEOF(attrs_to_test)];
1632     ATTR_TBL my_list[SIZEOF(attrs_to_test)];
1633     unsigned my_size = init_attr_list(my_list, termattrs());
1634
1635     if (my_size > 1) {
1636         for (j = 0; j < my_size; ++j) {
1637             my_wins[j] = subwin(stdscr,
1638                                 1, LEN_ATTRSTRING,
1639                                 2 + (int) (2 * j), COL_ATTRSTRING);
1640             scrollok(my_wins[j], FALSE);
1641         }
1642
1643         if (skip < 0)
1644             skip = 0;
1645
1646         n = skip;               /* make it easy */
1647         k = my_size - 1;
1648         init_attr_string();
1649
1650         do {
1651             int row = 2;
1652             chtype normal = A_NORMAL | BLANK;
1653             chtype extras = (chtype) ac;
1654
1655             if (use_colors) {
1656                 NCURSES_PAIRS_T pair = 0;
1657                 if ((fg != COLOR_BLACK) || (bg != COLOR_BLACK)) {
1658                     pair = 1;
1659                     if (init_pair(pair, fg, bg) == ERR) {
1660                         beep();
1661                     } else {
1662                         normal |= (chtype) COLOR_PAIR(pair);
1663                     }
1664                 }
1665                 if (tx >= 0) {
1666                     pair = 2;
1667                     if (init_pair(pair, tx, bg) == ERR) {
1668                         beep();
1669                     } else {
1670                         extras |= (chtype) COLOR_PAIR(pair);
1671                         normal &= ~A_COLOR;
1672                     }
1673                 }
1674             }
1675             bkgd(normal);
1676             bkgdset(normal);
1677             erase();
1678
1679             box(stdscr, 0, 0);
1680             MvAddStr(0, 20, "Character attribute test display");
1681
1682             for (j = 0; j < my_size; ++j) {
1683                 bool arrow = (j == k);
1684                 row = show_attr(my_wins[j], row, n, arrow,
1685                                 normal |
1686                                 extras |
1687                                 my_list[j].attr |
1688                                 my_list[k].attr,
1689                                 my_list[j].name);
1690             }
1691
1692             MvPrintw(row, COLS_PRE_ATTRS,
1693                      "This terminal does %shave the magic-cookie glitch",
1694                      get_xmc() > -1 ? "" : "not ");
1695             MvPrintw(row + 1, COLS_PRE_ATTRS, "Enter '?' for help.");
1696             show_color_attr(fg, bg, tx);
1697             printw("  ACS (%d)", ac != 0);
1698
1699             refresh();
1700         } while (attr_getc(&n, &fg, &bg, &tx, &ac, &k, my_size));
1701
1702         bkgdset(A_NORMAL | BLANK);
1703         erase();
1704         endwin();
1705     } else {
1706         Cannot("does not support video attributes.");
1707     }
1708 }
1709
1710 #if USE_WIDEC_SUPPORT
1711 static bool use_fullwidth;
1712 static wchar_t wide_attr_test_string[MAX_ATTRSTRING + 1];
1713
1714 #define FULL_LO 0xff00
1715 #define FULL_HI 0xff5e
1716 #define HALF_LO 0x20
1717
1718 #define isFullWidth(ch)   ((int)(ch) >= FULL_LO && (int)(ch) <= FULL_HI)
1719 #define ToNormalWidth(ch) (wchar_t) (((int)(ch) - FULL_LO) + HALF_LO)
1720 #define ToFullWidth(ch)   (wchar_t) (((int)(ch) - HALF_LO) + FULL_LO)
1721
1722 /*
1723  * Returns an ASCII code in [32..126]
1724  */
1725 static wchar_t
1726 normal_wchar(int ch)
1727 {
1728     wchar_t result = (wchar_t) ch;
1729     if (isFullWidth(ch))
1730         result = ToNormalWidth(ch);
1731     return result;
1732 }
1733
1734 /*
1735  * Returns either an ASCII code in in [32..126] or full-width in
1736  * [0xff00..0xff5e], according to use_fullwidth setting.
1737  */
1738 static wchar_t
1739 target_wchar(int ch)
1740 {
1741     wchar_t result = (wchar_t) ch;
1742     if (use_fullwidth) {
1743         if (!isFullWidth(ch))
1744             result = ToFullWidth(ch);
1745     } else {
1746         if (isFullWidth(ch))
1747             result = ToNormalWidth(ch);
1748     }
1749     return result;
1750 }
1751
1752 static void
1753 wide_adjust_attr_string(int adjust)
1754 {
1755     wchar_t save = wide_attr_test_string[0];
1756     int first = ((int) normal_wchar(save)) + adjust;
1757     int j, k;
1758
1759     if (first >= ATTRSTRING_1ST) {
1760         for (j = 0, k = first; j < MAX_ATTRSTRING; ++j, ++k) {
1761             if (k > ATTRSTRING_END)
1762                 break;
1763             wide_attr_test_string[j] = target_wchar(k);
1764             if (((k + 1 - first) % 5) == 0) {
1765                 if (++j >= MAX_ATTRSTRING)
1766                     break;
1767                 wide_attr_test_string[j] = ' ';
1768             }
1769         }
1770         if ((LEN_ATTRSTRING - j) > 5) {
1771             wide_attr_test_string[0] = save;
1772             wide_adjust_attr_string(adjust - 1);
1773         } else {
1774             while (j < MAX_ATTRSTRING)
1775                 wide_attr_test_string[j++] = ' ';
1776             wide_attr_test_string[j] = '\0';
1777         }
1778     }
1779 }
1780
1781 static void
1782 wide_init_attr_string(void)
1783 {
1784     use_fullwidth = FALSE;
1785     wide_attr_test_string[0] = (wchar_t) default_attr_string();
1786     wide_adjust_attr_string(0);
1787 }
1788
1789 static void
1790 set_wide_background(NCURSES_PAIRS_T pair)
1791 {
1792     cchar_t normal;
1793     wchar_t blank[2];
1794
1795     blank[0] = ' ';
1796     blank[1] = 0;
1797     setcchar(&normal, blank, A_NORMAL, pair, 0);
1798     bkgrnd(&normal);
1799     bkgrndset(&normal);
1800 }
1801
1802 static attr_t
1803 get_wide_background(void)
1804 {
1805     attr_t result = A_NORMAL;
1806     attr_t attr;
1807     cchar_t ch;
1808     NCURSES_PAIRS_T pair;
1809     wchar_t wch[10];
1810
1811     memset(&ch, 0, sizeof(ch));
1812     if (getbkgrnd(&ch) != ERR) {
1813         if (getcchar(&ch, wch, &attr, &pair, 0) != ERR) {
1814             result = attr;
1815         }
1816     }
1817     return result;
1818 }
1819
1820 static int
1821 wide_show_attr(WINDOW *win,
1822                int row,
1823                int skip,
1824                bool arrow,
1825                chtype attr,
1826                NCURSES_PAIRS_T pair,
1827                const char *name)
1828 {
1829     int ncv = get_ncv();
1830     chtype test = attr & ~WA_ALTCHARSET;
1831
1832     if (arrow)
1833         MvPrintw(row, COLS_PRE_ATTRS - 3, "-->");
1834     MvPrintw(row, COLS_PRE_ATTRS, "%s mode:", name);
1835     MvPrintw(row, COL_ATTRSTRING - 1, "|");
1836     if (skip)
1837         printw("%*s", skip, " ");
1838
1839     /*
1840      * Just for testing, write text using the alternate character set one
1841      * character at a time (to pass its rendition directly), and use the
1842      * string operation for the other attributes.
1843      */
1844     wmove(win, 0, 0);
1845     werase(win);
1846     if (attr & WA_ALTCHARSET) {
1847         const wchar_t *s;
1848         cchar_t ch;
1849
1850         for (s = wide_attr_test_string; *s != L'\0'; ++s) {
1851             wchar_t fill[2];
1852             fill[0] = *s;
1853             fill[1] = L'\0';
1854             setcchar(&ch, fill, attr, pair, 0);
1855             (void) wadd_wch(win, &ch);
1856         }
1857     } else {
1858         attr_t old_attr = 0;
1859         NCURSES_PAIRS_T old_pair = 0;
1860
1861         (void) (wattr_get) (win, &old_attr, &old_pair, 0);
1862         (void) wattr_set(win, attr, pair, 0);
1863         (void) waddwstr(win, wide_attr_test_string);
1864         (void) wattr_set(win, old_attr, old_pair, 0);
1865     }
1866     if (skip)
1867         printw("%*s", skip, " ");
1868     MvPrintw(row, COL_ATTRSTRING + LEN_ATTRSTRING, "|");
1869     if (test != A_NORMAL) {
1870         if (!(term_attrs() & test)) {
1871             printw(" (N/A)");
1872         } else {
1873             if (ncv > 0 && (get_wide_background() & A_COLOR)) {
1874                 static const attr_t table[] =
1875                 {
1876                     WA_STANDOUT,
1877                     WA_UNDERLINE,
1878                     WA_REVERSE,
1879                     WA_BLINK,
1880                     WA_DIM,
1881                     WA_BOLD,
1882                     WA_INVIS,
1883                     WA_PROTECT,
1884                     WA_ALTCHARSET
1885                 };
1886                 unsigned n;
1887                 bool found = FALSE;
1888                 for (n = 0; n < SIZEOF(table); n++) {
1889                     if ((table[n] & attr) != 0
1890                         && ((1 << n) & ncv) != 0) {
1891                         found = TRUE;
1892                         break;
1893                     }
1894                 }
1895                 if (found)
1896                     printw(" (NCV)");
1897             }
1898             if ((term_attrs() & test) != test) {
1899                 printw(" (Part)");
1900             }
1901         }
1902     }
1903     return row + 2;
1904 }
1905
1906 static bool
1907 wide_attr_getc(int *skip,
1908                NCURSES_COLOR_T *fg, NCURSES_COLOR_T *bg,
1909                NCURSES_COLOR_T *tx, int *ac,
1910                unsigned *kc, unsigned limit)
1911 {
1912     bool result = TRUE;
1913     bool error = FALSE;
1914     WINDOW *helpwin;
1915
1916     do {
1917         int ch = Getchar();
1918
1919         error = FALSE;
1920         if (ch < 256 && isdigit(ch)) {
1921             *skip = (ch - '0');
1922         } else {
1923             switch (ch) {
1924             case CTRL('L'):
1925                 Repaint();
1926                 break;
1927             case HELP_KEY_1:
1928                 if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
1929                     box_set(helpwin, 0, 0);
1930                     attr_legend(helpwin);
1931                     wGetchar(helpwin);
1932                     delwin(helpwin);
1933                 }
1934                 break;
1935             case 'a':
1936                 *ac = 0;
1937                 break;
1938             case 'A':
1939                 *ac = A_ALTCHARSET;
1940                 break;
1941             case 'v':
1942                 if (*kc == 0)
1943                     *kc = limit - 1;
1944                 else
1945                     *kc -= 1;
1946                 break;
1947             case 'V':
1948                 *kc += 1;
1949                 if (*kc >= limit)
1950                     *kc = 0;
1951                 break;
1952             case 'w':
1953                 use_fullwidth = FALSE;
1954                 wide_adjust_attr_string(0);
1955                 break;
1956             case 'W':
1957                 use_fullwidth = TRUE;
1958                 wide_adjust_attr_string(0);
1959                 break;
1960             case '<':
1961                 wide_adjust_attr_string(-1);
1962                 break;
1963             case '>':
1964                 wide_adjust_attr_string(1);
1965                 break;
1966             case case_QUIT:
1967                 result = FALSE;
1968                 break;
1969             default:
1970                 error = cycle_color_attr(ch, fg, bg, tx);
1971                 break;
1972             }
1973         }
1974     } while (error);
1975     return result;
1976 }
1977
1978 static void
1979 wide_attr_test(void)
1980 /* test text attributes using wide-character calls */
1981 {
1982     int n;
1983     int skip = get_xmc();
1984     NCURSES_COLOR_T fg = COLOR_BLACK;   /* color pair 0 is special */
1985     NCURSES_COLOR_T bg = COLOR_BLACK;
1986     NCURSES_COLOR_T tx = -1;
1987     int ac = 0;
1988     unsigned j, k;
1989     ATTR_TBL my_list[SIZEOF(attrs_to_test)];
1990     WINDOW *my_wins[SIZEOF(attrs_to_test)];
1991     unsigned my_size = init_attr_list(my_list, term_attrs());
1992
1993     if (my_size > 1) {
1994         for (j = 0; j < my_size; ++j) {
1995             my_wins[j] = subwin(stdscr,
1996                                 1, LEN_ATTRSTRING,
1997                                 2 + (int) (2 * j), COL_ATTRSTRING);
1998             scrollok(my_wins[j], FALSE);
1999         }
2000
2001         if (skip < 0)
2002             skip = 0;
2003
2004         n = skip;               /* make it easy */
2005         k = my_size - 1;
2006         wide_init_attr_string();
2007
2008         do {
2009             int row = 2;
2010             NCURSES_PAIRS_T pair = 0;
2011             NCURSES_PAIRS_T extras = 0;
2012
2013             if (use_colors) {
2014                 pair = (NCURSES_PAIRS_T) (fg != COLOR_BLACK || bg != COLOR_BLACK);
2015                 if (pair != 0) {
2016                     pair = 1;
2017                     if (init_pair(pair, fg, bg) == ERR) {
2018                         beep();
2019                     }
2020                 }
2021                 extras = pair;
2022                 if (tx >= 0) {
2023                     extras = 2;
2024                     if (init_pair(extras, tx, bg) == ERR) {
2025                         beep();
2026                     }
2027                 }
2028             }
2029             set_wide_background(pair);
2030             erase();
2031
2032             box_set(stdscr, 0, 0);
2033             MvAddStr(0, 20, "Character attribute test display");
2034
2035             for (j = 0; j < my_size; ++j) {
2036                 row = wide_show_attr(my_wins[j], row, n, (j == k),
2037                                      ((attr_t) ac |
2038                                       my_list[j].attr |
2039                                       my_list[k].attr),
2040                                      extras,
2041                                      my_list[j].name);
2042             }
2043
2044             MvPrintw(row, COLS_PRE_ATTRS,
2045                      "This terminal does %shave the magic-cookie glitch",
2046                      get_xmc() > -1 ? "" : "not ");
2047             MvPrintw(row + 1, COLS_PRE_ATTRS, "Enter '?' for help.");
2048             show_color_attr(fg, bg, tx);
2049             printw("  ACS (%d)", ac != 0);
2050
2051             refresh();
2052         } while (wide_attr_getc(&n, &fg, &bg, &tx, &ac, &k, my_size));
2053
2054         set_wide_background(0);
2055         erase();
2056         endwin();
2057     } else {
2058         Cannot("does not support extended video attributes.");
2059     }
2060 }
2061 #endif
2062
2063 /****************************************************************************
2064  *
2065  * Color support tests
2066  *
2067  ****************************************************************************/
2068
2069 static NCURSES_CONST char *the_color_names[] =
2070 {
2071     "black",
2072     "red",
2073     "green",
2074     "yellow",
2075     "blue",
2076     "magenta",
2077     "cyan",
2078     "white",
2079     "BLACK",
2080     "RED",
2081     "GREEN",
2082     "YELLOW",
2083     "BLUE",
2084     "MAGENTA",
2085     "CYAN",
2086     "WHITE"
2087 };
2088
2089 static void
2090 show_color_name(int y, int x, int color, bool wide)
2091 {
2092     if (move(y, x) != ERR) {
2093         char temp[80];
2094         int width = 8;
2095
2096         if (wide) {
2097             _nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp))
2098                         "%02d", color);
2099             width = 4;
2100         } else if (color >= 8) {
2101             _nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp))
2102                         "[%02d]", color);
2103         } else if (color < 0) {
2104             _nc_STRCPY(temp, "default", sizeof(temp));
2105         } else {
2106             _nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp))
2107                         "%.*s", 16, the_color_names[color]);
2108         }
2109         printw("%-*.*s", width, width, temp);
2110     }
2111 }
2112
2113 static void
2114 color_legend(WINDOW *helpwin, bool wide)
2115 {
2116     int row = 1;
2117     int col = 1;
2118
2119     MvWPrintw(helpwin, row++, col,
2120               "ESC to exit.");
2121     ++row;
2122     MvWPrintw(helpwin, row++, col,
2123               "Use up/down arrow to scroll through the display if it is");
2124     MvWPrintw(helpwin, row++, col,
2125               "longer than one screen. Control/N and Control/P can be used");
2126     MvWPrintw(helpwin, row++, col,
2127               "in place of up/down arrow.  Use pageup/pagedown to scroll a");
2128     MvWPrintw(helpwin, row++, col,
2129               "full screen; control/B and control/F can be used here.");
2130     ++row;
2131     MvWPrintw(helpwin, row++, col,
2132               "Toggles:");
2133     MvWPrintw(helpwin, row++, col,
2134               "  a/A     toggle altcharset off/on");
2135     MvWPrintw(helpwin, row++, col,
2136               "  b/B     toggle bold off/on");
2137     if (has_colors()) {
2138         MvWPrintw(helpwin, row++, col,
2139                   "  c/C     cycle used-colors through 8,16,...,COLORS");
2140     }
2141     MvWPrintw(helpwin, row++, col,
2142               "  n/N     toggle text/number on/off");
2143     MvWPrintw(helpwin, row++, col,
2144               "  r/R     toggle reverse on/off");
2145     MvWPrintw(helpwin, row++, col,
2146               "  w/W     toggle width between 8/16 colors");
2147 #if USE_WIDEC_SUPPORT
2148     if (wide) {
2149         MvWPrintw(helpwin, row++, col,
2150                   "Wide characters:");
2151         MvWPrintw(helpwin, row, col,
2152                   "  x/X     toggle text between ASCII and wide-character");
2153     }
2154 #else
2155     (void) wide;
2156 #endif
2157 }
2158
2159 #define set_color_test(name, value) if (name != value) { name = value; base_row = 0; }
2160
2161 static int
2162 color_cycle(int current, int step)
2163 {
2164     int result = current;
2165     if (step < 0) {
2166         if (current <= 8) {
2167             result = COLORS;
2168         } else {
2169             result = 8;
2170             if ((result * 2) > COLORS) {
2171                 result = COLORS;
2172             } else {
2173                 while ((result * 2) < current) {
2174                     result *= 2;
2175                 }
2176             }
2177         }
2178     } else {
2179         if (current >= COLORS) {
2180             result = 8;
2181         } else {
2182             result *= 2;
2183         }
2184         if (result > COLORS)
2185             result = COLORS;
2186     }
2187     return result;
2188 }
2189
2190 /* generate a color test pattern */
2191 static void
2192 color_test(void)
2193 {
2194     NCURSES_PAIRS_T i;
2195     int top = 0, width;
2196     int base_row = 0;
2197     int grid_top = top + 3;
2198     int page_size = (LINES - grid_top);
2199     int pairs_max;
2200     int colors_max = COLORS;
2201     int col_limit;
2202     int row_limit;
2203     int per_row;
2204     char *numbered = 0;
2205     const char *hello;
2206     bool done = FALSE;
2207     bool opt_acsc = FALSE;
2208     bool opt_bold = FALSE;
2209     bool opt_revs = FALSE;
2210     bool opt_nums = FALSE;
2211     bool opt_wide = FALSE;
2212     WINDOW *helpwin;
2213
2214     numbered = (char *) calloc((size_t) (COLS + 1), sizeof(char));
2215     done = ((COLS < 16) || (numbered == 0));
2216
2217     /*
2218      * Because the number of colors is usually a power of two, we also use
2219      * a power of two for the number of colors shown per line (to be tidy).
2220      */
2221     for (col_limit = 1; col_limit * 2 < COLS; col_limit *= 2) ;
2222
2223     while (!done) {
2224         int shown = 0;
2225
2226         pairs_max = PAIR_NUMBER(A_COLOR) + 1;
2227         if (colors_max * colors_max <= COLOR_PAIRS) {
2228             int limit = (colors_max - min_colors) * (colors_max - min_colors);
2229             if (pairs_max > limit)
2230                 pairs_max = limit;
2231         } else {
2232             if (pairs_max > COLOR_PAIRS)
2233                 pairs_max = COLOR_PAIRS;
2234         }
2235
2236         /* this assumes an 80-column line */
2237         if (opt_wide) {
2238             width = 4;
2239             hello = "Test";
2240             per_row = (col_limit / ((colors_max > 8) ? 4 : 8));
2241         } else {
2242             width = 8;
2243             hello = "Hello";
2244             per_row = (col_limit / 8);
2245         }
2246         per_row -= min_colors;
2247
2248         row_limit = (pairs_max + per_row - 1) / per_row;
2249
2250         move(0, 0);
2251         (void) printw("There are %d color pairs and %d colors",
2252                       pairs_max, COLORS);
2253         if (colors_max != COLORS)
2254             (void) printw(" (using %d colors)", colors_max);
2255         if (min_colors)
2256             (void) addstr(" besides 'default'");
2257
2258         clrtobot();
2259         MvPrintw(top + 1, 0,
2260                  "%dx%d matrix of foreground/background colors, bold *%s*\n",
2261                  row_limit,
2262                  per_row,
2263                  opt_bold ? "on" : "off");
2264
2265         /* show color names/numbers across the top */
2266         for (i = 0; i < per_row; i++)
2267             show_color_name(top + 2, (i + 1) * width, i + min_colors, opt_wide);
2268
2269         /* show a grid of colors, with color names/ numbers on the left */
2270         for (i = (NCURSES_PAIRS_T) (base_row * per_row); i < pairs_max; i++) {
2271             int row = grid_top + (i / per_row) - base_row;
2272             int col = (i % per_row + 1) * width;
2273             NCURSES_PAIRS_T pair = i;
2274
2275             if ((i / per_row) > row_limit)
2276                 break;
2277
2278 #define InxToFG(i) (NCURSES_COLOR_T) ((i % (colors_max - min_colors)) + min_colors)
2279 #define InxToBG(i) (NCURSES_COLOR_T) ((i / (colors_max - min_colors)) + min_colors)
2280             if (row >= 0 && move(row, col) != ERR) {
2281                 NCURSES_COLOR_T fg = InxToFG(i);
2282                 NCURSES_COLOR_T bg = InxToBG(i);
2283
2284                 init_pair(pair, fg, bg);
2285                 attron(COLOR_PAIR(pair));
2286                 if (opt_acsc)
2287                     attron(A_ALTCHARSET);
2288                 if (opt_bold)
2289                     attron(A_BOLD);
2290                 if (opt_revs)
2291                     attron(A_REVERSE);
2292
2293                 if (opt_nums) {
2294                     _nc_SPRINTF(numbered, _nc_SLIMIT((size_t) (COLS + 1))
2295                                 "{%02X}", (int) i);
2296                     hello = numbered;
2297                 }
2298                 printw("%-*.*s", width, width, hello);
2299                 (void) attrset(A_NORMAL);
2300
2301                 if ((i % per_row) == 0 && InxToFG(i) == min_colors) {
2302                     show_color_name(row, 0, InxToBG(i), opt_wide);
2303                 }
2304                 ++shown;
2305             } else if (shown) {
2306                 break;
2307             }
2308         }
2309
2310         switch (wGetchar(stdscr)) {
2311         case 'a':
2312             opt_acsc = FALSE;
2313             break;
2314         case 'A':
2315             opt_acsc = TRUE;
2316             break;
2317         case 'b':
2318             opt_bold = FALSE;
2319             break;
2320         case 'B':
2321             opt_bold = TRUE;
2322             break;
2323         case 'c':
2324             colors_max = color_cycle(colors_max, -1);
2325             break;
2326         case 'C':
2327             colors_max = color_cycle(colors_max, 1);
2328             break;
2329         case 'n':
2330             opt_nums = FALSE;
2331             break;
2332         case 'N':
2333             opt_nums = TRUE;
2334             break;
2335         case 'r':
2336             opt_revs = FALSE;
2337             break;
2338         case 'R':
2339             opt_revs = TRUE;
2340             break;
2341         case case_QUIT:
2342             done = TRUE;
2343             continue;
2344         case 'w':
2345             set_color_test(opt_wide, FALSE);
2346             break;
2347         case 'W':
2348             set_color_test(opt_wide, TRUE);
2349             break;
2350         case CTRL('p'):
2351         case KEY_UP:
2352             if (base_row <= 0) {
2353                 beep();
2354             } else {
2355                 base_row -= 1;
2356             }
2357             break;
2358         case CTRL('n'):
2359         case KEY_DOWN:
2360             if (base_row + page_size >= row_limit) {
2361                 beep();
2362             } else {
2363                 base_row += 1;
2364             }
2365             break;
2366         case CTRL('b'):
2367         case KEY_PREVIOUS:
2368         case KEY_PPAGE:
2369             if (base_row <= 0) {
2370                 beep();
2371             } else {
2372                 base_row -= (page_size - 1);
2373                 if (base_row < 0)
2374                     base_row = 0;
2375             }
2376             break;
2377         case CTRL('f'):
2378         case KEY_NEXT:
2379         case KEY_NPAGE:
2380             if (base_row + page_size >= row_limit) {
2381                 beep();
2382             } else {
2383                 base_row += page_size - 1;
2384                 if (base_row + page_size >= row_limit) {
2385                     base_row = row_limit - page_size - 1;
2386                 }
2387             }
2388             break;
2389         case HELP_KEY_1:
2390             if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
2391                 box(helpwin, 0, 0);
2392                 color_legend(helpwin, FALSE);
2393                 wGetchar(helpwin);
2394                 delwin(helpwin);
2395             }
2396             break;
2397         default:
2398             beep();
2399             continue;
2400         }
2401     }
2402
2403     erase();
2404     endwin();
2405
2406     free(numbered);
2407 }
2408
2409 #if USE_WIDEC_SUPPORT
2410 /* generate a color test pattern */
2411 static void
2412 wide_color_test(void)
2413 {
2414     int i;
2415     int top = 0, width;
2416     int base_row = 0;
2417     int grid_top = top + 3;
2418     int page_size = (LINES - grid_top);
2419     int pairs_max = (unsigned short) (-1);
2420     int colors_max = COLORS;
2421     int col_limit;
2422     int row_limit;
2423     int per_row;
2424     char *numbered = 0;
2425     const char *hello;
2426     bool done = FALSE;
2427     bool opt_acsc = FALSE;
2428     bool opt_bold = FALSE;
2429     bool opt_revs = FALSE;
2430     bool opt_wide = FALSE;
2431     bool opt_nums = FALSE;
2432     bool opt_xchr = FALSE;
2433     wchar_t *buffer = 0;
2434     WINDOW *helpwin;
2435
2436     numbered = (char *) calloc((size_t) (COLS + 1), sizeof(char));
2437     buffer = (wchar_t *) calloc((size_t) (COLS + 1), sizeof(wchar_t));
2438     done = ((COLS < 16) || (numbered == 0) || (buffer == 0));
2439
2440     /*
2441      * Because the number of colors is usually a power of two, we also use
2442      * a power of two for the number of colors shown per line (to be tidy).
2443      */
2444     for (col_limit = 1; col_limit * 2 < COLS; col_limit *= 2) ;
2445
2446     while (!done) {
2447         int shown = 0;
2448
2449         pairs_max = (unsigned short) (-1);
2450         if (colors_max * colors_max <= COLOR_PAIRS) {
2451             int limit = (colors_max - min_colors) * (colors_max - min_colors);
2452             if (pairs_max > limit)
2453                 pairs_max = limit;
2454         } else {
2455             if (pairs_max > COLOR_PAIRS)
2456                 pairs_max = COLOR_PAIRS;
2457         }
2458
2459         if (opt_wide) {
2460             width = 4;
2461             hello = "Test";
2462             per_row = (col_limit / ((colors_max > 8) ? 4 : 8));
2463         } else {
2464             width = 8;
2465             hello = "Hello";
2466             per_row = (col_limit / 8);
2467         }
2468         per_row -= min_colors;
2469
2470         if (opt_xchr) {
2471             make_fullwidth_text(buffer, hello);
2472             width *= 2;
2473             per_row /= 2;
2474         } else {
2475             make_narrow_text(buffer, hello);
2476         }
2477
2478         row_limit = (pairs_max + per_row - 1) / per_row;
2479
2480         move(0, 0);
2481         (void) printw("There are %d color pairs and %d colors",
2482                       pairs_max, COLORS);
2483         if (colors_max != COLORS)
2484             (void) printw(" (using %d colors)", colors_max);
2485         if (min_colors)
2486             (void) addstr(" besides 'default'");
2487
2488         clrtobot();
2489         MvPrintw(top + 1, 0,
2490                  "%dx%d matrix of foreground/background colors, bold *%s*\n",
2491                  row_limit,
2492                  per_row,
2493                  opt_bold ? "on" : "off");
2494
2495         /* show color names/numbers across the top */
2496         for (i = 0; i < per_row; i++)
2497             show_color_name(top + 2, (i + 1) * width, i + min_colors, opt_wide);
2498
2499         /* show a grid of colors, with color names/ numbers on the left */
2500         for (i = (base_row * per_row); i < pairs_max; i++) {
2501             int row = grid_top + (i / per_row) - base_row;
2502             int col = (i % per_row + 1) * width;
2503             NCURSES_PAIRS_T pair = (NCURSES_PAIRS_T) i;
2504
2505             if ((i / per_row) > row_limit)
2506                 break;
2507
2508             if (row >= 0 && move(row, col) != ERR) {
2509                 init_pair(pair, InxToFG(i), InxToBG(i));
2510                 (void) color_set(pair, NULL);
2511                 if (opt_acsc)
2512                     attr_on(A_ALTCHARSET, NULL);
2513                 if (opt_bold)
2514                     attr_on(A_BOLD, NULL);
2515                 if (opt_revs)
2516                     attr_on(A_REVERSE, NULL);
2517
2518                 if (opt_nums) {
2519                     _nc_SPRINTF(numbered,
2520                                 _nc_SLIMIT((size_t) (COLS + 1) * sizeof(wchar_t))
2521                                 "{%02X}", i);
2522                     if (opt_xchr) {
2523                         make_fullwidth_text(buffer, numbered);
2524                     } else {
2525                         make_narrow_text(buffer, numbered);
2526                     }
2527                 }
2528                 addnwstr(buffer, width);
2529                 (void) attr_set(A_NORMAL, 0, NULL);
2530
2531                 if ((i % per_row) == 0 && InxToFG(i) == min_colors) {
2532                     show_color_name(row, 0, InxToBG(i), opt_wide);
2533                 }
2534                 ++shown;
2535             } else if (shown) {
2536                 break;
2537             }
2538         }
2539
2540         switch (wGetchar(stdscr)) {
2541         case 'a':
2542             opt_acsc = FALSE;
2543             break;
2544         case 'A':
2545             opt_acsc = TRUE;
2546             break;
2547         case 'b':
2548             opt_bold = FALSE;
2549             break;
2550         case 'B':
2551             opt_bold = TRUE;
2552             break;
2553         case 'c':
2554             colors_max = color_cycle(colors_max, -1);
2555             break;
2556         case 'C':
2557             colors_max = color_cycle(colors_max, 1);
2558             break;
2559         case 'n':
2560             opt_nums = FALSE;
2561             break;
2562         case 'N':
2563             opt_nums = TRUE;
2564             break;
2565         case 'r':
2566             opt_revs = FALSE;
2567             break;
2568         case 'R':
2569             opt_revs = TRUE;
2570             break;
2571         case case_QUIT:
2572             done = TRUE;
2573             continue;
2574         case 'w':
2575             set_color_test(opt_wide, FALSE);
2576             break;
2577         case 'W':
2578             set_color_test(opt_wide, TRUE);
2579             break;
2580         case 'x':
2581             opt_xchr = FALSE;
2582             break;
2583         case 'X':
2584             opt_xchr = TRUE;
2585             break;
2586         case CTRL('p'):
2587         case KEY_UP:
2588             if (base_row <= 0) {
2589                 beep();
2590             } else {
2591                 base_row -= 1;
2592             }
2593             break;
2594         case CTRL('n'):
2595         case KEY_DOWN:
2596             if (base_row + page_size >= row_limit) {
2597                 beep();
2598             } else {
2599                 base_row += 1;
2600             }
2601             break;
2602         case CTRL('b'):
2603         case KEY_PREVIOUS:
2604         case KEY_PPAGE:
2605             if (base_row <= 0) {
2606                 beep();
2607             } else {
2608                 base_row -= (page_size - 1);
2609                 if (base_row < 0)
2610                     base_row = 0;
2611             }
2612             break;
2613         case CTRL('f'):
2614         case KEY_NEXT:
2615         case KEY_NPAGE:
2616             if (base_row + page_size >= row_limit) {
2617                 beep();
2618             } else {
2619                 base_row += page_size - 1;
2620                 if (base_row + page_size >= row_limit) {
2621                     base_row = row_limit - page_size - 1;
2622                 }
2623             }
2624             break;
2625         case HELP_KEY_1:
2626             if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
2627                 box(helpwin, 0, 0);
2628                 color_legend(helpwin, TRUE);
2629                 wGetchar(helpwin);
2630                 delwin(helpwin);
2631             }
2632             break;
2633         default:
2634             beep();
2635             continue;
2636         }
2637     }
2638
2639     erase();
2640     endwin();
2641
2642     free(numbered);
2643     free(buffer);
2644 }
2645 #endif /* USE_WIDEC_SUPPORT */
2646
2647 static void
2648 change_color(NCURSES_PAIRS_T current, int field, int value, int usebase)
2649 {
2650     NCURSES_COLOR_T red, green, blue;
2651
2652     color_content(current, &red, &green, &blue);
2653
2654     switch (field) {
2655     case 0:
2656         red = (NCURSES_COLOR_T) (usebase ? (red + value) : value);
2657         break;
2658     case 1:
2659         green = (NCURSES_COLOR_T) (usebase ? (green + value) : value);
2660         break;
2661     case 2:
2662         blue = (NCURSES_COLOR_T) (usebase ? (blue + value) : value);
2663         break;
2664     }
2665
2666     if (init_color(current, red, green, blue) == ERR)
2667         beep();
2668 }
2669
2670 static void
2671 reset_all_colors(void)
2672 {
2673     NCURSES_PAIRS_T c;
2674
2675     for (c = 0; c < COLORS; ++c)
2676         init_color(c,
2677                    all_colors[c].red,
2678                    all_colors[c].green,
2679                    all_colors[c].blue);
2680 }
2681
2682 #define okCOLOR(n) ((n) >= 0 && (n) < max_colors)
2683 #define okRGB(n)   ((n) >= 0 && (n) <= 1000)
2684 #define DecodeRGB(n) (NCURSES_COLOR_T) ((n * 1000) / 0xffff)
2685
2686 static void
2687 init_all_colors(bool xterm_colors, char *palette_file)
2688 {
2689     NCURSES_PAIRS_T cp;
2690     all_colors = typeMalloc(RGB_DATA, (unsigned) max_colors);
2691     if (!all_colors)
2692         failed("all_colors");
2693     for (cp = 0; cp < max_colors; ++cp) {
2694         color_content(cp,
2695                       &all_colors[cp].red,
2696                       &all_colors[cp].green,
2697                       &all_colors[cp].blue);
2698     }
2699     /* xterm and compatible terminals can read results of an OSC string
2700      * asking for the current color palette.
2701      */
2702     if (xterm_colors) {
2703         int n;
2704         int got;
2705         char result[BUFSIZ];
2706         int check_n;
2707         unsigned check_r, check_g, check_b;
2708
2709         raw();
2710         noecho();
2711         for (n = 0; n < max_colors; ++n) {
2712             fprintf(stderr, "\033]4;%d;?\007", n);
2713             got = (int) read(0, result, sizeof(result) - 1);
2714             if (got < 0)
2715                 break;
2716             result[got] = '\0';
2717             if (sscanf(result, "\033]4;%d;rgb:%x/%x/%x\007",
2718                        &check_n,
2719                        &check_r,
2720                        &check_g,
2721                        &check_b) == 4 &&
2722                 check_n == n) {
2723                 all_colors[n].red = DecodeRGB(check_r);
2724                 all_colors[n].green = DecodeRGB(check_g);
2725                 all_colors[n].blue = DecodeRGB(check_b);
2726             } else {
2727                 break;
2728             }
2729         }
2730         reset_prog_mode();
2731     }
2732     if (palette_file != 0) {
2733         FILE *fp = fopen(palette_file, "r");
2734         if (fp != 0) {
2735             char buffer[BUFSIZ];
2736             int red, green, blue;
2737             int scale = 1000;
2738             int c;
2739             while (fgets(buffer, sizeof(buffer), fp) != 0) {
2740                 if (sscanf(buffer, "scale:%d", &c) == 1) {
2741                     scale = c;
2742                 } else if (sscanf(buffer, "%d:%d %d %d",
2743                                   &c,
2744                                   &red,
2745                                   &green,
2746                                   &blue) == 4
2747                            && okCOLOR(c)
2748                            && okRGB(red)
2749                            && okRGB(green)
2750                            && okRGB(blue)) {
2751 #define Scaled(n) (NCURSES_COLOR_T) (((n) * 1000) / scale)
2752                     all_colors[c].red = Scaled(red);
2753                     all_colors[c].green = Scaled(green);
2754                     all_colors[c].blue = Scaled(blue);
2755                 }
2756             }
2757             fclose(fp);
2758         }
2759     }
2760 }
2761
2762 #define scaled_rgb(n) ((255 * (n)) / 1000)
2763
2764 static void
2765 color_edit(void)
2766 /* display the color test pattern, without trying to edit colors */
2767 {
2768     int i;
2769     int current;
2770     int this_c, value, field;
2771     int last_c;
2772     int top_color;
2773     int page_size;
2774
2775     reset_all_colors();
2776 #ifdef KEY_RESIZE
2777   retry:
2778 #endif
2779     current = 0;
2780     this_c = 0;
2781     value = 0;
2782     field = 0;
2783     top_color = 0;
2784     page_size = (LINES - 6);
2785     erase();
2786
2787     for (i = 0; i < max_colors; i++)
2788         init_pair((NCURSES_PAIRS_T) i,
2789                   (NCURSES_COLOR_T) COLOR_WHITE,
2790                   (NCURSES_COLOR_T) i);
2791
2792     MvPrintw(LINES - 2, 0, "Number: %d", value);
2793
2794     do {
2795         NCURSES_COLOR_T red, green, blue;
2796
2797         attron(A_BOLD);
2798         MvAddStr(0, 20, "Color RGB Value Editing");
2799         attroff(A_BOLD);
2800
2801         for (i = (NCURSES_COLOR_T) top_color;
2802              (i - top_color < page_size)
2803              && (i < max_colors); i++) {
2804             char numeric[80];
2805
2806             _nc_SPRINTF(numeric, _nc_SLIMIT(sizeof(numeric)) "[%d]", i);
2807             MvPrintw(2 + i - top_color, 0, "%c %-8s:",
2808                      (i == current ? '>' : ' '),
2809                      (i < (int) SIZEOF(the_color_names)
2810                       ? the_color_names[i] : numeric));
2811             (void) attrset(AttrArg(COLOR_PAIR(i), 0));
2812             addstr("        ");
2813             (void) attrset(A_NORMAL);
2814
2815             color_content((NCURSES_PAIRS_T) i, &red, &green, &blue);
2816             addstr("   R = ");
2817             if (current == i && field == 0)
2818                 attron(A_STANDOUT);
2819             printw("%04d", (int) red);
2820             if (current == i && field == 0)
2821                 (void) attrset(A_NORMAL);
2822             addstr(", G = ");
2823             if (current == i && field == 1)
2824                 attron(A_STANDOUT);
2825             printw("%04d", (int) green);
2826             if (current == i && field == 1)
2827                 (void) attrset(A_NORMAL);
2828             addstr(", B = ");
2829             if (current == i && field == 2)
2830                 attron(A_STANDOUT);
2831             printw("%04d", (int) blue);
2832             if (current == i && field == 2)
2833                 (void) attrset(A_NORMAL);
2834             (void) attrset(A_NORMAL);
2835             printw(" ( %3d %3d %3d )",
2836                    (int) scaled_rgb(red),
2837                    (int) scaled_rgb(green),
2838                    (int) scaled_rgb(blue));
2839         }
2840
2841         MvAddStr(LINES - 3, 0,
2842                  "Use up/down to select a color, left/right to change fields.");
2843         MvAddStr(LINES - 2, 0,
2844                  "Modify field by typing nnn=, nnn-, or nnn+.  ? for help.");
2845
2846         move(2 + current - top_color, 0);
2847
2848         last_c = this_c;
2849         this_c = Getchar();
2850         if (this_c < 256 && isdigit(this_c) && !isdigit(last_c))
2851             value = 0;
2852
2853         switch (this_c) {
2854 #ifdef KEY_RESIZE
2855         case KEY_RESIZE:
2856             move(0, 0);
2857             goto retry;
2858 #endif
2859         case '!':
2860             ShellOut(FALSE);
2861             /* FALLTHRU */
2862         case CTRL('r'):
2863             endwin();
2864             refresh();
2865             break;
2866         case CTRL('l'):
2867             refresh();
2868             break;
2869         case CTRL('b'):
2870         case KEY_PPAGE:
2871             if (current > 0)
2872                 current -= (page_size - 1);
2873             else
2874                 beep();
2875             break;
2876
2877         case CTRL('f'):
2878         case KEY_NPAGE:
2879             if (current < (max_colors - 1))
2880                 current += (page_size - 1);
2881             else
2882                 beep();
2883             break;
2884
2885         case CTRL('p'):
2886         case KEY_UP:
2887             current = (current == 0 ? (max_colors - 1) : current - 1);
2888             break;
2889
2890         case CTRL('n'):
2891         case KEY_DOWN:
2892             current = (current == (max_colors - 1) ? 0 : current + 1);
2893             break;
2894
2895         case '\t':
2896         case KEY_RIGHT:
2897             field = (field == 2 ? 0 : field + 1);
2898             break;
2899
2900         case KEY_BTAB:
2901         case KEY_LEFT:
2902             field = (field == 0 ? 2 : field - 1);
2903             break;
2904
2905         case '0':
2906         case '1':
2907         case '2':
2908         case '3':
2909         case '4':
2910         case '5':
2911         case '6':
2912         case '7':
2913         case '8':
2914         case '9':
2915             value = value * 10 + (this_c - '0');
2916             break;
2917
2918         case '+':
2919             change_color((NCURSES_PAIRS_T) current, field, value, 1);
2920             break;
2921
2922         case '-':
2923             change_color((NCURSES_PAIRS_T) current, field, -value, 1);
2924             break;
2925
2926         case '=':
2927             change_color((NCURSES_PAIRS_T) current, field, value, 0);
2928             break;
2929
2930         case HELP_KEY_1:
2931             erase();
2932             P("                      RGB Value Editing Help");
2933             P("");
2934             P("You are in the RGB value editor.  Use the arrow keys to select one of");
2935             P("the fields in one of the RGB triples of the current colors; the one");
2936             P("currently selected will be reverse-video highlighted.");
2937             P("");
2938             P("To change a field, enter the digits of the new value; they are echoed");
2939             P("as entered.  Finish by typing `='.  The change will take effect instantly.");
2940             P("To increment or decrement a value, use the same procedure, but finish");
2941             P("with a `+' or `-'.");
2942             P("");
2943             P("Use `!' to shell-out, ^R or ^L to repaint the screen.");
2944             P("");
2945             P("Press 'm' to invoke the top-level menu with the current color settings.");
2946             P("To quit, do ESC");
2947
2948             Pause();
2949             erase();
2950             break;
2951
2952         case 'm':
2953             endwin();
2954             main_menu(FALSE);
2955             for (i = 0; i < max_colors; i++)
2956                 init_pair((NCURSES_PAIRS_T) i,
2957                           (NCURSES_COLOR_T) COLOR_WHITE,
2958                           (NCURSES_COLOR_T) i);
2959             refresh();
2960             break;
2961
2962         case case_QUIT:
2963             break;
2964
2965         default:
2966             beep();
2967             break;
2968         }
2969
2970         if (current < 0)
2971             current = 0;
2972         if (current >= max_colors)
2973             current = max_colors - 1;
2974         if (current < top_color)
2975             top_color = current;
2976         if (current - top_color >= page_size)
2977             top_color = current - (page_size - 1);
2978
2979         MvPrintw(LINES - 1, 0, "Number: %d", value);
2980         clrtoeol();
2981     } while
2982         (!isQuit(this_c, TRUE));
2983
2984     erase();
2985
2986     /*
2987      * ncurses does not reset each color individually when calling endwin().
2988      */
2989     reset_all_colors();
2990
2991     endwin();
2992 }
2993
2994 /****************************************************************************
2995  *
2996  * Alternate character-set stuff
2997  *
2998  ****************************************************************************/
2999 static bool
3000 cycle_attr(int ch, unsigned *at_code, chtype *attr, ATTR_TBL * list, unsigned limit)
3001 {
3002     bool result = TRUE;
3003
3004     switch (ch) {
3005     case 'v':
3006         if ((*at_code += 1) >= limit)
3007             *at_code = 0;
3008         break;
3009     case 'V':
3010         if (*at_code == 0)
3011             *at_code = limit - 1;
3012         else
3013             *at_code -= 1;
3014         break;
3015     default:
3016         result = FALSE;
3017         break;
3018     }
3019     if (result)
3020         *attr = list[*at_code].attr;
3021     return result;
3022 }
3023
3024 static bool
3025 cycle_colors(int ch, int *fg, int *bg, NCURSES_PAIRS_T *pair)
3026 {
3027     bool result = FALSE;
3028
3029     if (use_colors) {
3030         result = TRUE;
3031         switch (ch) {
3032         case 'F':
3033             if ((*fg -= 1) < 0)
3034                 *fg = COLORS - 1;
3035             break;
3036         case 'f':
3037             if ((*fg += 1) >= COLORS)
3038                 *fg = 0;
3039             break;
3040         case 'B':
3041             if ((*bg -= 1) < 0)
3042                 *bg = COLORS - 1;
3043             break;
3044         case 'b':
3045             if ((*bg += 1) >= COLORS)
3046                 *bg = 0;
3047             break;
3048         default:
3049             result = FALSE;
3050             break;
3051         }
3052         if (result) {
3053             *pair = (NCURSES_PAIRS_T) (*fg != COLOR_BLACK || *bg != COLOR_BLACK);
3054             if (*pair != 0) {
3055                 *pair = 1;
3056                 if (init_pair(*pair,
3057                               (NCURSES_COLOR_T) *fg,
3058                               (NCURSES_COLOR_T) *bg) == ERR) {
3059                     result = FALSE;
3060                 }
3061             }
3062         }
3063     }
3064     return result;
3065 }
3066
3067 /****************************************************************************
3068  *
3069  * Soft-key label test
3070  *
3071  ****************************************************************************/
3072
3073 #if USE_SOFTKEYS
3074
3075 #define SLK_HELP 17
3076 #define SLK_WORK (SLK_HELP + 3)
3077
3078 static void
3079 slk_help(void)
3080 {
3081     static const char *table[] =
3082     {
3083         "Available commands are:"
3084         ,""
3085         ,"^L         -- repaint this message and activate soft keys"
3086         ,"a/d        -- activate/disable soft keys"
3087         ,"c          -- set centered format for labels"
3088         ,"l          -- set left-justified format for labels"
3089         ,"r          -- set right-justified format for labels"
3090         ,"[12345678] -- set label; labels are numbered 1 through 8"
3091         ,"e          -- erase stdscr (should not erase labels)"
3092         ,"s          -- test scrolling of shortened screen"
3093         ,"v/V        -- cycle through video attributes"
3094 #if HAVE_SLK_COLOR
3095         ,"F/f/B/b    -- cycle through foreground/background colors"
3096 #endif
3097         ,"ESC        -- return to main menu"
3098         ,""
3099         ,"Note: if activating the soft keys causes your terminal to scroll up"
3100         ,"one line, your terminal auto-scrolls when anything is written to the"
3101         ,"last screen position.  The ncurses code does not yet handle this"
3102         ,"gracefully."
3103     };
3104     unsigned j;
3105
3106     move(2, 0);
3107     for (j = 0; j < SIZEOF(table); ++j) {
3108         P(table[j]);
3109     }
3110     refresh();
3111 }
3112
3113 #if HAVE_SLK_COLOR
3114 static void
3115 call_slk_color(int fg, int bg)
3116 {
3117     init_pair(1, (NCURSES_COLOR_T) bg, (NCURSES_COLOR_T) fg);
3118     slk_color(1);
3119     MvPrintw(SLK_WORK, 0, "Colors %d/%d\n", fg, bg);
3120     clrtoeol();
3121     slk_touch();
3122     slk_noutrefresh();
3123     refresh();
3124 }
3125 #endif
3126
3127 static void
3128 slk_test(void)
3129 /* exercise the soft keys */
3130 {
3131     int c, fmt = 1;
3132     char buf[9];
3133     char *s;
3134     chtype attr = A_NORMAL;
3135     unsigned at_code = 0;
3136 #if HAVE_SLK_COLOR
3137     int fg = COLOR_BLACK;
3138     int bg = COLOR_WHITE;
3139     NCURSES_PAIRS_T pair = 0;
3140 #endif
3141     ATTR_TBL my_list[SIZEOF(attrs_to_test)];
3142     unsigned my_size = init_attr_list(my_list, termattrs());
3143
3144     c = CTRL('l');
3145 #if HAVE_SLK_COLOR
3146     if (use_colors) {
3147         call_slk_color(fg, bg);
3148     }
3149 #endif
3150
3151     do {
3152         move(0, 0);
3153         switch (c) {
3154         case CTRL('l'):
3155             erase();
3156             attron(A_BOLD);
3157             MvAddStr(0, 20, "Soft Key Exerciser");
3158             attroff(A_BOLD);
3159
3160             slk_help();
3161             /* fall through */
3162
3163         case 'a':
3164             slk_restore();
3165             break;
3166
3167         case 'e':
3168             wclear(stdscr);
3169             break;
3170
3171         case 's':
3172             MvPrintw(SLK_WORK, 0, "Press Q to stop the scrolling-test: ");
3173             while ((c = Getchar()) != 'Q' && (c != ERR))
3174                 AddCh(c);
3175             break;
3176
3177         case 'd':
3178             slk_clear();
3179             break;
3180
3181         case 'l':
3182             fmt = 0;
3183             break;
3184
3185         case 'c':
3186             fmt = 1;
3187             break;
3188
3189         case 'r':
3190             fmt = 2;
3191             break;
3192
3193         case '1':
3194         case '2':
3195         case '3':
3196         case '4':
3197         case '5':
3198         case '6':
3199         case '7':
3200         case '8':
3201             MvAddStr(SLK_WORK, 0, "Please enter the label value: ");
3202             _nc_STRCPY(buf, "", sizeof(buf));
3203             if ((s = slk_label(c - '0')) != 0) {
3204                 _nc_STRNCPY(buf, s, (size_t) 8);
3205             }
3206             wGetstring(stdscr, buf, 8);
3207             slk_set((c - '0'), buf, fmt);
3208             slk_refresh();
3209             move(SLK_WORK, 0);
3210             clrtobot();
3211             break;
3212
3213         case case_QUIT:
3214             goto done;
3215
3216 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
3217         case KEY_RESIZE:
3218             wnoutrefresh(stdscr);
3219             break;
3220 #endif
3221
3222         default:
3223             if (cycle_attr(c, &at_code, &attr, my_list, my_size)) {
3224                 slk_attrset(attr);
3225                 slk_touch();
3226                 slk_noutrefresh();
3227                 break;
3228             }
3229 #if HAVE_SLK_COLOR
3230             if (cycle_colors(c, &fg, &bg, &pair)) {
3231                 if (use_colors) {
3232                     call_slk_color(fg, bg);
3233                 } else {
3234                     beep();
3235                 }
3236                 break;
3237             }
3238 #endif
3239             beep();
3240             break;
3241         }
3242     } while (!isQuit(c = Getchar(), TRUE));
3243
3244   done:
3245     slk_clear();
3246     erase();
3247     endwin();
3248 }
3249
3250 #if USE_WIDEC_SUPPORT
3251 #define SLKLEN 8
3252 static void
3253 wide_slk_test(void)
3254 /* exercise the soft keys */
3255 {
3256     int c, fmt = 1;
3257     wchar_t buf[SLKLEN + 1];
3258     char *s;
3259     chtype attr = A_NORMAL;
3260     unsigned at_code = 0;
3261     int fg = COLOR_BLACK;
3262     int bg = COLOR_WHITE;
3263     NCURSES_PAIRS_T pair = 0;
3264     ATTR_TBL my_list[SIZEOF(attrs_to_test)];
3265     unsigned my_size = init_attr_list(my_list, term_attrs());
3266
3267     c = CTRL('l');
3268     if (use_colors) {
3269         call_slk_color(fg, bg);
3270     }
3271     do {
3272         move(0, 0);
3273         switch (c) {
3274         case CTRL('l'):
3275             erase();
3276             attr_on(WA_BOLD, NULL);
3277             MvAddStr(0, 20, "Soft Key Exerciser");
3278             attr_off(WA_BOLD, NULL);
3279
3280             slk_help();
3281             /* fall through */
3282
3283         case 'a':
3284             slk_restore();
3285             break;
3286
3287         case 'e':
3288             wclear(stdscr);
3289             break;
3290
3291         case 's':
3292             MvPrintw(SLK_WORK, 0, "Press Q to stop the scrolling-test: ");
3293             while ((c = Getchar()) != 'Q' && (c != ERR))
3294                 AddCh(c);
3295             break;
3296
3297         case 'd':
3298             slk_clear();
3299             break;
3300
3301         case 'l':
3302             fmt = 0;
3303             break;
3304
3305         case 'c':
3306             fmt = 1;
3307             break;
3308
3309         case 'r':
3310             fmt = 2;
3311             break;
3312
3313         case '1':
3314         case '2':
3315         case '3':
3316         case '4':
3317         case '5':
3318         case '6':
3319         case '7':
3320         case '8':
3321             MvAddStr(SLK_WORK, 0, "Please enter the label value: ");
3322             *buf = 0;
3323             if ((s = slk_label(c - '0')) != 0) {
3324                 char *temp = strdup(s);
3325                 size_t used = strlen(temp);
3326                 size_t want = SLKLEN;
3327                 size_t test;
3328 #ifndef state_unused
3329                 mbstate_t state;
3330 #endif
3331
3332                 buf[0] = L'\0';
3333                 while (want > 0 && used != 0) {
3334                     const char *base = s;
3335                     reset_mbytes(state);
3336                     test = count_mbytes(base, 0, &state);
3337                     if (test == (size_t) -1) {
3338                         temp[--used] = 0;
3339                     } else if (test > want) {
3340                         temp[--used] = 0;
3341                     } else {
3342                         reset_mbytes(state);
3343                         trans_mbytes(buf, base, want, &state);
3344                         break;
3345                     }
3346                 }
3347                 free(temp);
3348             }
3349             wGet_wstring(stdscr, buf, SLKLEN);
3350             slk_wset((c - '0'), buf, fmt);
3351             slk_refresh();
3352             move(SLK_WORK, 0);
3353             clrtobot();
3354             break;
3355
3356         case case_QUIT:
3357             goto done;
3358
3359         case 'F':
3360             if (use_colors) {
3361                 fg = (NCURSES_COLOR_T) ((fg + 1) % COLORS);
3362                 call_slk_color(fg, bg);
3363             }
3364             break;
3365         case 'B':
3366             if (use_colors) {
3367                 bg = (NCURSES_COLOR_T) ((bg + 1) % COLORS);
3368                 call_slk_color(fg, bg);
3369             }
3370             break;
3371 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
3372         case KEY_RESIZE:
3373             wnoutrefresh(stdscr);
3374             break;
3375 #endif
3376         default:
3377             if (cycle_attr(c, &at_code, &attr, my_list, my_size)) {
3378                 slk_attr_set(attr, (NCURSES_COLOR_T) (fg || bg), NULL);
3379                 slk_touch();
3380                 slk_noutrefresh();
3381                 break;
3382             }
3383 #if HAVE_SLK_COLOR
3384             if (cycle_colors(c, &fg, &bg, &pair)) {
3385                 if (use_colors) {
3386                     call_slk_color(fg, bg);
3387                 } else {
3388                     beep();
3389                 }
3390                 break;
3391             }
3392 #endif
3393             beep();
3394             break;
3395         }
3396     } while (!isQuit(c = Getchar(), TRUE));
3397
3398   done:
3399     slk_clear();
3400     erase();
3401     endwin();
3402 }
3403 #endif
3404 #endif /* SLK_INIT */
3405
3406 static void
3407 show_256_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3408 {
3409     unsigned first = 0;
3410     unsigned last = 255;
3411     unsigned code;
3412     int count;
3413
3414     erase();
3415     attron(A_BOLD);
3416     MvPrintw(0, 20, "Display of Character Codes %#0x to %#0x",
3417              first, last);
3418     attroff(A_BOLD);
3419     refresh();
3420
3421     for (code = first; code <= last; ++code) {
3422         int row = (int) (2 + (code / 16));
3423         int col = (int) (5 * (code % 16));
3424         IGNORE_RC(mvaddch(row, col, colored_chtype(code, attr, pair)));
3425         for (count = 1; count < repeat; ++count) {
3426             AddCh(colored_chtype(code, attr, pair));
3427         }
3428     }
3429
3430 }
3431
3432 /*
3433  * Show a slice of 32 characters, allowing those to be repeated up to the
3434  * screen's width.
3435  *
3436  * ISO 6429:  codes 0x80 to 0x9f may be control characters that cause the
3437  * terminal to perform functions.  The remaining codes can be graphic.
3438  */
3439 static void
3440 show_upper_chars(int base, int pagesize, int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3441 {
3442     unsigned code;
3443     unsigned first = (unsigned) base;
3444     unsigned last = first + (unsigned) pagesize - 2;
3445     bool C1 = (first == 128);
3446     int reply;
3447
3448     erase();
3449     attron(A_BOLD);
3450     MvPrintw(0, 20, "Display of %s Character Codes %d to %d",
3451              C1 ? "C1" : "GR", first, last);
3452     attroff(A_BOLD);
3453     refresh();
3454
3455     for (code = first; code <= last; code++) {
3456         int count = repeat;
3457         int row = 2 + ((int) (code - first) % (pagesize / 2));
3458         int col = ((int) (code - first) / (pagesize / 2)) * COLS / 2;
3459         char tmp[80];
3460         _nc_SPRINTF(tmp, _nc_SLIMIT(sizeof(tmp)) "%3u (0x%x)", code, code);
3461         MvPrintw(row, col, "%*s: ", COLS / 4, tmp);
3462
3463         do {
3464             if (C1)
3465                 nodelay(stdscr, TRUE);
3466             echochar(colored_chtype(code, attr, pair));
3467             if (C1) {
3468                 /* (yes, this _is_ crude) */
3469                 while ((reply = Getchar()) != ERR) {
3470                     AddCh(UChar(reply));
3471                     napms(10);
3472                 }
3473                 nodelay(stdscr, FALSE);
3474             }
3475         } while (--count > 0);
3476     }
3477 }
3478
3479 #define PC_COLS 4
3480
3481 static void
3482 show_pc_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3483 {
3484     unsigned code;
3485
3486     erase();
3487     attron(A_BOLD);
3488     MvPrintw(0, 20, "Display of PC Character Codes");
3489     attroff(A_BOLD);
3490     refresh();
3491
3492     for (code = 0; code < 16; ++code) {
3493         MvPrintw(2, (int) code * PC_COLS + 8, "%X", code);
3494     }
3495     for (code = 0; code < 256; code++) {
3496         int count = repeat;
3497         int row = 3 + (int) (code / 16) + (code >= 128);
3498         int col = 8 + (int) (code % 16) * PC_COLS;
3499         if ((code % 16) == 0)
3500             MvPrintw(row, 0, "0x%02x:", code);
3501         move(row, col);
3502         do {
3503             switch (code) {
3504             case '\n':
3505             case '\r':
3506             case '\b':
3507             case '\f':
3508             case '\033':
3509             case 0x9b:
3510                 /*
3511                  * Skip the ones that do not work.
3512                  */
3513                 break;
3514             default:
3515                 AddCh(colored_chtype(code, A_ALTCHARSET | attr, pair));
3516                 break;
3517             }
3518         } while (--count > 0);
3519     }
3520 }
3521
3522 static void
3523 show_box_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3524 {
3525     (void) repeat;
3526
3527     attr |= (attr_t) COLOR_PAIR(pair);
3528
3529     erase();
3530     attron(A_BOLD);
3531     MvAddStr(0, 20, "Display of the ACS Line-Drawing Set");
3532     attroff(A_BOLD);
3533     refresh();
3534     /* *INDENT-OFF* */
3535     wborder(stdscr,
3536             colored_chtype(ACS_VLINE,    attr, pair),
3537             colored_chtype(ACS_VLINE,    attr, pair),
3538             colored_chtype(ACS_HLINE,    attr, pair),
3539             colored_chtype(ACS_HLINE,    attr, pair),
3540             colored_chtype(ACS_ULCORNER, attr, pair),
3541             colored_chtype(ACS_URCORNER, attr, pair),
3542             colored_chtype(ACS_LLCORNER, attr, pair),
3543             colored_chtype(ACS_LRCORNER, attr, pair));
3544     MvHLine(LINES / 2, 0,        colored_chtype(ACS_HLINE, attr, pair), COLS);
3545     MvVLine(0,         COLS / 2, colored_chtype(ACS_VLINE, attr, pair), LINES);
3546     MvAddCh(0,         COLS / 2, colored_chtype(ACS_TTEE,  attr, pair));
3547     MvAddCh(LINES / 2, COLS / 2, colored_chtype(ACS_PLUS,  attr, pair));
3548     MvAddCh(LINES - 1, COLS / 2, colored_chtype(ACS_BTEE,  attr, pair));
3549     MvAddCh(LINES / 2, 0,        colored_chtype(ACS_LTEE,  attr, pair));
3550     MvAddCh(LINES / 2, COLS - 1, colored_chtype(ACS_RTEE,  attr, pair));
3551     /* *INDENT-ON* */
3552
3553 }
3554
3555 static int
3556 show_1_acs(int n, int repeat, const char *name, chtype code)
3557 {
3558     const int height = 16;
3559     int row = 2 + (n % height);
3560     int col = (n / height) * COLS / 2;
3561
3562     MvPrintw(row, col, "%*s : ", COLS / 4, name);
3563     do {
3564         AddCh(code);
3565     } while (--repeat > 0);
3566     return n + 1;
3567 }
3568
3569 static void
3570 show_acs_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3571 /* display the ACS character set */
3572 {
3573     int n;
3574
3575 #define BOTH(name) #name, colored_chtype(name, attr, (chtype) pair)
3576
3577     erase();
3578     attron(A_BOLD);
3579     MvAddStr(0, 20, "Display of the ACS Character Set");
3580     attroff(A_BOLD);
3581     refresh();
3582
3583     n = show_1_acs(0, repeat, BOTH(ACS_ULCORNER));
3584     n = show_1_acs(n, repeat, BOTH(ACS_URCORNER));
3585     n = show_1_acs(n, repeat, BOTH(ACS_LLCORNER));
3586     n = show_1_acs(n, repeat, BOTH(ACS_LRCORNER));
3587
3588     n = show_1_acs(n, repeat, BOTH(ACS_LTEE));
3589     n = show_1_acs(n, repeat, BOTH(ACS_RTEE));
3590     n = show_1_acs(n, repeat, BOTH(ACS_TTEE));
3591     n = show_1_acs(n, repeat, BOTH(ACS_BTEE));
3592
3593     n = show_1_acs(n, repeat, BOTH(ACS_HLINE));
3594     n = show_1_acs(n, repeat, BOTH(ACS_VLINE));
3595
3596     /*
3597      * HPUX's ACS definitions are broken here.  Just give up.
3598      */
3599 #if !(defined(__hpux) && !defined(NCURSES_VERSION))
3600     n = show_1_acs(n, repeat, BOTH(ACS_LARROW));
3601     n = show_1_acs(n, repeat, BOTH(ACS_RARROW));
3602     n = show_1_acs(n, repeat, BOTH(ACS_UARROW));
3603     n = show_1_acs(n, repeat, BOTH(ACS_DARROW));
3604
3605     n = show_1_acs(n, repeat, BOTH(ACS_BLOCK));
3606     n = show_1_acs(n, repeat, BOTH(ACS_BOARD));
3607     n = show_1_acs(n, repeat, BOTH(ACS_LANTERN));
3608     n = show_1_acs(n, repeat, BOTH(ACS_BULLET));
3609     n = show_1_acs(n, repeat, BOTH(ACS_CKBOARD));
3610     n = show_1_acs(n, repeat, BOTH(ACS_DEGREE));
3611     n = show_1_acs(n, repeat, BOTH(ACS_DIAMOND));
3612     n = show_1_acs(n, repeat, BOTH(ACS_PLMINUS));
3613     n = show_1_acs(n, repeat, BOTH(ACS_PLUS));
3614
3615     n = show_1_acs(n, repeat, BOTH(ACS_GEQUAL));
3616     n = show_1_acs(n, repeat, BOTH(ACS_NEQUAL));
3617     n = show_1_acs(n, repeat, BOTH(ACS_LEQUAL));
3618
3619     n = show_1_acs(n, repeat, BOTH(ACS_STERLING));
3620     n = show_1_acs(n, repeat, BOTH(ACS_PI));
3621     n = show_1_acs(n, repeat, BOTH(ACS_S1));
3622     n = show_1_acs(n, repeat, BOTH(ACS_S3));
3623     n = show_1_acs(n, repeat, BOTH(ACS_S7));
3624     (void) show_1_acs(n, repeat, BOTH(ACS_S9));
3625 #endif
3626 }
3627
3628 static void
3629 acs_display(void)
3630 {
3631     int c = 'a';
3632     int pagesize = 32;
3633     char *term = getenv("TERM");
3634     const char *pch_kludge = ((term != 0 && strstr(term, "linux"))
3635                               ? "p=PC, "
3636                               : "");
3637     chtype attr = A_NORMAL;
3638     int digit = 0;
3639     int repeat = 1;
3640     int fg = COLOR_BLACK;
3641     int bg = COLOR_BLACK;
3642     unsigned at_code = 0;
3643     NCURSES_PAIRS_T pair = 0;
3644     void (*last_show_acs) (int, attr_t, NCURSES_PAIRS_T) = 0;
3645     ATTR_TBL my_list[SIZEOF(attrs_to_test)];
3646     unsigned my_size = init_attr_list(my_list, termattrs());
3647
3648     do {
3649         switch (c) {
3650         case CTRL('L'):
3651             Repaint();
3652             break;
3653         case 'a':
3654             ToggleAcs(last_show_acs, show_acs_chars);
3655             break;
3656         case 'p':
3657             if (*pch_kludge)
3658                 ToggleAcs(last_show_acs, show_pc_chars);
3659             else
3660                 beep();
3661             break;
3662         case 'w':
3663             if (pagesize == 32) {
3664                 pagesize = 256;
3665             } else {
3666                 pagesize = 32;
3667             }
3668             break;
3669         case 'x':
3670             ToggleAcs(last_show_acs, show_box_chars);
3671             break;
3672         case '0':
3673         case '1':
3674         case '2':
3675         case '3':
3676             digit = (c - '0');
3677             last_show_acs = 0;
3678             break;
3679         case '-':
3680             if (digit > 0) {
3681                 --digit;
3682                 last_show_acs = 0;
3683             } else {
3684                 beep();
3685             }
3686             break;
3687         case '+':
3688             if (digit < 3) {
3689                 ++digit;
3690                 last_show_acs = 0;
3691             } else {
3692                 beep();
3693             }
3694             break;
3695         case '>':
3696             if (repeat < (COLS / 4))
3697                 ++repeat;
3698             break;
3699         case '<':
3700             if (repeat > 1)
3701                 --repeat;
3702             break;
3703         default:
3704             if (cycle_attr(c, &at_code, &attr, my_list, my_size)
3705                 || cycle_colors(c, &fg, &bg, &pair)) {
3706                 break;
3707             } else {
3708                 beep();
3709             }
3710             break;
3711         }
3712         if (pagesize != 32) {
3713             show_256_chars(repeat, attr, pair);
3714         } else if (last_show_acs != 0) {
3715             last_show_acs(repeat, attr, pair);
3716         } else {
3717             show_upper_chars(digit * pagesize + 128, pagesize, repeat, attr, pair);
3718         }
3719
3720         MvPrintw(LINES - 3, 0,
3721                  "Note: ANSI terminals may not display C1 characters.");
3722         MvPrintw(LINES - 2, 0,
3723                  "Select: a=ACS, w=all x=box, %s0=C1, 1-3,+/- non-ASCII, </> repeat, ESC=quit",
3724                  pch_kludge);
3725         if (use_colors) {
3726             MvPrintw(LINES - 1, 0,
3727                      "v/V, f/F, b/B cycle through video attributes (%s) and color %d/%d.",
3728                      my_list[at_code].name,
3729                      fg, bg);
3730         } else {
3731             MvPrintw(LINES - 1, 0,
3732                      "v/V cycles through video attributes (%s).",
3733                      my_list[at_code].name);
3734         }
3735         refresh();
3736     } while (!isQuit(c = Getchar(), TRUE));
3737
3738     Pause();
3739     erase();
3740     endwin();
3741 }
3742
3743 #if USE_WIDEC_SUPPORT
3744 static cchar_t *
3745 merge_wide_attr(cchar_t *dst, const cchar_t *src, attr_t attr, NCURSES_PAIRS_T pair)
3746 {
3747     int count;
3748
3749     *dst = *src;
3750     do {
3751         TEST_CCHAR(src, count, {
3752             attr |= (test_attrs & A_ALTCHARSET);
3753             setcchar(dst, test_wch, attr, pair, NULL);
3754         }
3755         , {
3756             ;
3757         });
3758     } while (0);
3759     return dst;
3760 }
3761
3762 /*
3763  * Header/legend take up no more than 8 lines, leaving 16 lines on a 24-line
3764  * display.  If there are no repeats, we could normally display 16 lines of 64
3765  * characters (1024 total).  However, taking repeats and double-width cells
3766  * into account, use 256 characters for the page.
3767  */
3768 static void
3769 show_paged_widechars(int base,
3770                      int pagesize,
3771                      int repeat,
3772                      int space,
3773                      attr_t attr,
3774                      NCURSES_PAIRS_T pair)
3775 {
3776     int first = base * pagesize;
3777     int last = first + pagesize - 1;
3778     int per_line = 16;
3779     cchar_t temp;
3780     wchar_t code;
3781     wchar_t codes[10];
3782
3783     erase();
3784     attron(A_BOLD);
3785     MvPrintw(0, 20, "Display of Character Codes %#x to %#x", first, last);
3786     attroff(A_BOLD);
3787
3788     for (code = (wchar_t) first; (int) code <= last; code++) {
3789         int row = (2 + ((int) code - first) / per_line);
3790         int col = 5 * ((int) code % per_line);
3791         int count;
3792
3793         memset(&codes, 0, sizeof(codes));
3794         codes[0] = code;
3795         setcchar(&temp, codes, attr, pair, 0);
3796         move(row, col);
3797         if (wcwidth(code) == 0 && code != 0) {
3798             AddCh((chtype) space |
3799                   (A_REVERSE ^ attr) |
3800                   (attr_t) COLOR_PAIR(pair));
3801         }
3802         add_wch(&temp);
3803         for (count = 1; count < repeat; ++count) {
3804             add_wch(&temp);
3805         }
3806     }
3807 }
3808
3809 static void
3810 show_upper_widechars(int first, int repeat, int space, attr_t attr, NCURSES_PAIRS_T pair)
3811 {
3812     cchar_t temp;
3813     wchar_t code;
3814     int last = first + 31;
3815
3816     erase();
3817     attron(A_BOLD);
3818     MvPrintw(0, 20, "Display of Character Codes %d to %d", first, last);
3819     attroff(A_BOLD);
3820
3821     for (code = (wchar_t) first; (int) code <= last; code++) {
3822         int row = 2 + ((code - first) % 16);
3823         int col = ((code - first) / 16) * COLS / 2;
3824         wchar_t codes[10];
3825         char tmp[80];
3826         int count = repeat;
3827         int y, x;
3828
3829         _nc_SPRINTF(tmp, _nc_SLIMIT(sizeof(tmp))
3830                     "%3ld (0x%lx)", (long) code, (long) code);
3831         MvPrintw(row, col, "%*s: ", COLS / 4, tmp);
3832
3833         memset(&codes, 0, sizeof(codes));
3834         codes[0] = code;
3835         setcchar(&temp, codes, attr, pair, 0);
3836
3837         do {
3838             /*
3839              * Give non-spacing characters something to combine with.  If we
3840              * don't, they'll bunch up in a heap on the space after the ":".
3841              * Mark them with reverse-video to make them simpler to find on
3842              * the display.
3843              */
3844             if (wcwidth(code) == 0) {
3845                 AddCh((chtype) space |
3846                       (A_REVERSE ^ attr) |
3847                       (attr_t) COLOR_PAIR(pair));
3848             }
3849             /*
3850              * This uses echo_wchar(), for comparison with the normal 'f'
3851              * test (and to make a test-case for echo_wchar()).  The screen
3852              * may flicker because the erase() at the top of the function
3853              * is met by the builtin refresh() in echo_wchar().
3854              */
3855             echo_wchar(&temp);
3856             /*
3857              * The repeat-count may make text wrap - avoid that.
3858              */
3859             getyx(stdscr, y, x);
3860             (void) y;
3861             if (x >= col + (COLS / 2) - 2)
3862                 break;
3863         } while (--count > 0);
3864     }
3865 }
3866
3867 static int
3868 show_1_wacs(int n, int repeat, const char *name, const cchar_t *code)
3869 {
3870     const int height = 16;
3871     int row = 2 + (n % height);
3872     int col = (n / height) * COLS / 2;
3873
3874     MvPrintw(row, col, "%*s : ", COLS / 4, name);
3875     while (--repeat >= 0) {
3876         add_wch(code);
3877     }
3878     return n + 1;
3879 }
3880
3881 #define MERGE_ATTR(wch) merge_wide_attr(&temp, wch, attr, pair)
3882
3883 static void
3884 show_wacs_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3885 /* display the wide-ACS character set */
3886 {
3887     cchar_t temp;
3888
3889     int n;
3890
3891 /*#define BOTH2(name) #name, &(name) */
3892 #define BOTH2(name) #name, MERGE_ATTR(name)
3893
3894     erase();
3895     attron(A_BOLD);
3896     MvAddStr(0, 20, "Display of the Wide-ACS Character Set");
3897     attroff(A_BOLD);
3898     refresh();
3899
3900     n = show_1_wacs(0, repeat, BOTH2(WACS_ULCORNER));
3901     n = show_1_wacs(n, repeat, BOTH2(WACS_URCORNER));
3902     n = show_1_wacs(n, repeat, BOTH2(WACS_LLCORNER));
3903     n = show_1_wacs(n, repeat, BOTH2(WACS_LRCORNER));
3904
3905     n = show_1_wacs(n, repeat, BOTH2(WACS_LTEE));
3906     n = show_1_wacs(n, repeat, BOTH2(WACS_RTEE));
3907     n = show_1_wacs(n, repeat, BOTH2(WACS_TTEE));
3908     n = show_1_wacs(n, repeat, BOTH2(WACS_BTEE));
3909
3910     n = show_1_wacs(n, repeat, BOTH2(WACS_HLINE));
3911     n = show_1_wacs(n, repeat, BOTH2(WACS_VLINE));
3912
3913     n = show_1_wacs(n, repeat, BOTH2(WACS_LARROW));
3914     n = show_1_wacs(n, repeat, BOTH2(WACS_RARROW));
3915     n = show_1_wacs(n, repeat, BOTH2(WACS_UARROW));
3916     n = show_1_wacs(n, repeat, BOTH2(WACS_DARROW));
3917
3918     n = show_1_wacs(n, repeat, BOTH2(WACS_BLOCK));
3919     n = show_1_wacs(n, repeat, BOTH2(WACS_BOARD));
3920     n = show_1_wacs(n, repeat, BOTH2(WACS_LANTERN));
3921     n = show_1_wacs(n, repeat, BOTH2(WACS_BULLET));
3922     n = show_1_wacs(n, repeat, BOTH2(WACS_CKBOARD));
3923     n = show_1_wacs(n, repeat, BOTH2(WACS_DEGREE));
3924     n = show_1_wacs(n, repeat, BOTH2(WACS_DIAMOND));
3925     n = show_1_wacs(n, repeat, BOTH2(WACS_PLMINUS));
3926     n = show_1_wacs(n, repeat, BOTH2(WACS_PLUS));
3927
3928 #ifdef CURSES_WACS_ARRAY
3929     n = show_1_wacs(n, repeat, BOTH2(WACS_GEQUAL));
3930     n = show_1_wacs(n, repeat, BOTH2(WACS_NEQUAL));
3931     n = show_1_wacs(n, repeat, BOTH2(WACS_LEQUAL));
3932
3933     n = show_1_wacs(n, repeat, BOTH2(WACS_STERLING));
3934     n = show_1_wacs(n, repeat, BOTH2(WACS_PI));
3935     n = show_1_wacs(n, repeat, BOTH2(WACS_S1));
3936     n = show_1_wacs(n, repeat, BOTH2(WACS_S3));
3937     n = show_1_wacs(n, repeat, BOTH2(WACS_S7));
3938     (void) show_1_wacs(n, repeat, BOTH2(WACS_S9));
3939 #endif
3940 }
3941
3942 #ifdef WACS_D_PLUS
3943 static void
3944 show_wacs_chars_double(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3945 /* display the wide-ACS character set */
3946 {
3947     cchar_t temp;
3948
3949     int n;
3950
3951 /*#define BOTH2(name) #name, &(name) */
3952 #define BOTH2(name) #name, MERGE_ATTR(name)
3953
3954     erase();
3955     attron(A_BOLD);
3956     MvAddStr(0, 20, "Display of the Wide-ACS Character Set");
3957     attroff(A_BOLD);
3958     refresh();
3959
3960     n = show_1_wacs(0, repeat, BOTH2(WACS_D_ULCORNER));
3961     n = show_1_wacs(n, repeat, BOTH2(WACS_D_URCORNER));
3962     n = show_1_wacs(n, repeat, BOTH2(WACS_D_LLCORNER));
3963     n = show_1_wacs(n, repeat, BOTH2(WACS_D_LRCORNER));
3964
3965     n = show_1_wacs(n, repeat, BOTH2(WACS_D_LTEE));
3966     n = show_1_wacs(n, repeat, BOTH2(WACS_D_RTEE));
3967     n = show_1_wacs(n, repeat, BOTH2(WACS_D_TTEE));
3968     n = show_1_wacs(n, repeat, BOTH2(WACS_D_BTEE));
3969
3970     n = show_1_wacs(n, repeat, BOTH2(WACS_D_HLINE));
3971     n = show_1_wacs(n, repeat, BOTH2(WACS_D_VLINE));
3972
3973     n = show_1_wacs(n, repeat, BOTH2(WACS_LARROW));
3974     n = show_1_wacs(n, repeat, BOTH2(WACS_RARROW));
3975     n = show_1_wacs(n, repeat, BOTH2(WACS_UARROW));
3976     n = show_1_wacs(n, repeat, BOTH2(WACS_DARROW));
3977
3978     n = show_1_wacs(n, repeat, BOTH2(WACS_BLOCK));
3979     n = show_1_wacs(n, repeat, BOTH2(WACS_BOARD));
3980     n = show_1_wacs(n, repeat, BOTH2(WACS_LANTERN));
3981     n = show_1_wacs(n, repeat, BOTH2(WACS_BULLET));
3982     n = show_1_wacs(n, repeat, BOTH2(WACS_CKBOARD));
3983     n = show_1_wacs(n, repeat, BOTH2(WACS_DEGREE));
3984     n = show_1_wacs(n, repeat, BOTH2(WACS_DIAMOND));
3985     n = show_1_wacs(n, repeat, BOTH2(WACS_PLMINUS));
3986     n = show_1_wacs(n, repeat, BOTH2(WACS_D_PLUS));
3987
3988 #ifdef CURSES_WACS_ARRAY
3989     n = show_1_wacs(n, repeat, BOTH2(WACS_GEQUAL));
3990     n = show_1_wacs(n, repeat, BOTH2(WACS_NEQUAL));
3991     n = show_1_wacs(n, repeat, BOTH2(WACS_LEQUAL));
3992
3993     n = show_1_wacs(n, repeat, BOTH2(WACS_STERLING));
3994     n = show_1_wacs(n, repeat, BOTH2(WACS_PI));
3995     n = show_1_wacs(n, repeat, BOTH2(WACS_S1));
3996     n = show_1_wacs(n, repeat, BOTH2(WACS_S3));
3997     n = show_1_wacs(n, repeat, BOTH2(WACS_S7));
3998     (void) show_1_wacs(n, repeat, BOTH2(WACS_S9));
3999 #endif
4000 }
4001 #endif
4002
4003 #ifdef WACS_T_PLUS
4004 static void
4005 show_wacs_chars_thick(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
4006 /* display the wide-ACS character set */
4007 {
4008     cchar_t temp;
4009
4010     int n;
4011
4012 /*#define BOTH2(name) #name, &(name) */
4013 #define BOTH2(name) #name, MERGE_ATTR(name)
4014
4015     erase();
4016     attron(A_BOLD);
4017     MvAddStr(0, 20, "Display of the Wide-ACS Character Set");
4018     attroff(A_BOLD);
4019     refresh();
4020
4021     n = show_1_wacs(0, repeat, BOTH2(WACS_T_ULCORNER));
4022     n = show_1_wacs(n, repeat, BOTH2(WACS_T_URCORNER));
4023     n = show_1_wacs(n, repeat, BOTH2(WACS_T_LLCORNER));
4024     n = show_1_wacs(n, repeat, BOTH2(WACS_T_LRCORNER));
4025
4026     n = show_1_wacs(n, repeat, BOTH2(WACS_T_LTEE));
4027     n = show_1_wacs(n, repeat, BOTH2(WACS_T_RTEE));
4028     n = show_1_wacs(n, repeat, BOTH2(WACS_T_TTEE));
4029     n = show_1_wacs(n, repeat, BOTH2(WACS_T_BTEE));
4030
4031     n = show_1_wacs(n, repeat, BOTH2(WACS_T_HLINE));
4032     n = show_1_wacs(n, repeat, BOTH2(WACS_T_VLINE));
4033
4034     n = show_1_wacs(n, repeat, BOTH2(WACS_LARROW));
4035     n = show_1_wacs(n, repeat, BOTH2(WACS_RARROW));
4036     n = show_1_wacs(n, repeat, BOTH2(WACS_UARROW));
4037     n = show_1_wacs(n, repeat, BOTH2(WACS_DARROW));
4038
4039     n = show_1_wacs(n, repeat, BOTH2(WACS_BLOCK));
4040     n = show_1_wacs(n, repeat, BOTH2(WACS_BOARD));
4041     n = show_1_wacs(n, repeat, BOTH2(WACS_LANTERN));
4042     n = show_1_wacs(n, repeat, BOTH2(WACS_BULLET));
4043     n = show_1_wacs(n, repeat, BOTH2(WACS_CKBOARD));
4044     n = show_1_wacs(n, repeat, BOTH2(WACS_DEGREE));
4045     n = show_1_wacs(n, repeat, BOTH2(WACS_DIAMOND));
4046     n = show_1_wacs(n, repeat, BOTH2(WACS_PLMINUS));
4047     n = show_1_wacs(n, repeat, BOTH2(WACS_T_PLUS));
4048
4049 #ifdef CURSES_WACS_ARRAY
4050     n = show_1_wacs(n, repeat, BOTH2(WACS_GEQUAL));
4051     n = show_1_wacs(n, repeat, BOTH2(WACS_NEQUAL));
4052     n = show_1_wacs(n, repeat, BOTH2(WACS_LEQUAL));
4053
4054     n = show_1_wacs(n, repeat, BOTH2(WACS_STERLING));
4055     n = show_1_wacs(n, repeat, BOTH2(WACS_PI));
4056     n = show_1_wacs(n, repeat, BOTH2(WACS_S1));
4057     n = show_1_wacs(n, repeat, BOTH2(WACS_S3));
4058     n = show_1_wacs(n, repeat, BOTH2(WACS_S7));
4059     (void) show_1_wacs(n, repeat, BOTH2(WACS_S9));
4060 #endif
4061 }
4062 #endif
4063
4064 #undef MERGE_ATTR
4065
4066 #define MERGE_ATTR(n,wch) merge_wide_attr(&temp[n], wch, attr, pair)
4067
4068 static void
4069 show_wbox_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
4070 {
4071     cchar_t temp[8];
4072
4073     (void) repeat;
4074     erase();
4075     attron(A_BOLD);
4076     MvAddStr(0, 20, "Display of the Wide-ACS Line-Drawing Set");
4077     attroff(A_BOLD);
4078     refresh();
4079
4080     wborder_set(stdscr,
4081                 MERGE_ATTR(0, WACS_VLINE),
4082                 MERGE_ATTR(1, WACS_VLINE),
4083                 MERGE_ATTR(2, WACS_HLINE),
4084                 MERGE_ATTR(3, WACS_HLINE),
4085                 MERGE_ATTR(4, WACS_ULCORNER),
4086                 MERGE_ATTR(5, WACS_URCORNER),
4087                 MERGE_ATTR(6, WACS_LLCORNER),
4088                 MERGE_ATTR(7, WACS_LRCORNER));
4089     /* *INDENT-OFF* */
4090     (void) mvhline_set(LINES / 2, 0,        MERGE_ATTR(0, WACS_HLINE), COLS);
4091     (void) mvvline_set(0,         COLS / 2, MERGE_ATTR(0, WACS_VLINE), LINES);
4092     (void) mvadd_wch(0,           COLS / 2, MERGE_ATTR(0, WACS_TTEE));
4093     (void) mvadd_wch(LINES / 2,   COLS / 2, MERGE_ATTR(0, WACS_PLUS));
4094     (void) mvadd_wch(LINES - 1,   COLS / 2, MERGE_ATTR(0, WACS_BTEE));
4095     (void) mvadd_wch(LINES / 2,   0,        MERGE_ATTR(0, WACS_LTEE));
4096     (void) mvadd_wch(LINES / 2,   COLS - 1, MERGE_ATTR(0, WACS_RTEE));
4097     /* *INDENT-ON* */
4098
4099 }
4100
4101 #undef MERGE_ATTR
4102
4103 static int
4104 show_2_wacs(int n, const char *name, const char *code, attr_t attr, NCURSES_PAIRS_T pair)
4105 {
4106     const int height = 16;
4107     int row = 2 + (n % height);
4108     int col = (n / height) * COLS / 2;
4109     char temp[80];
4110
4111     MvPrintw(row, col, "%*s : ", COLS / 4, name);
4112     (void) attr_set(attr, pair, 0);
4113     _nc_STRNCPY(temp, code, 20);
4114     addstr(temp);
4115     (void) attr_set(A_NORMAL, 0, 0);
4116     return n + 1;
4117 }
4118
4119 #define SHOW_UTF8(n, name, code) show_2_wacs(n, name, code, attr, pair)
4120
4121 static void
4122 show_utf8_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
4123 {
4124     int n;
4125
4126     (void) repeat;
4127     erase();
4128     attron(A_BOLD);
4129     MvAddStr(0, 20, "Display of the Wide-ACS Character Set");
4130     attroff(A_BOLD);
4131     refresh();
4132     /* *INDENT-OFF* */
4133     n = SHOW_UTF8(0, "WACS_ULCORNER",   "\342\224\214");
4134     n = SHOW_UTF8(n, "WACS_URCORNER",   "\342\224\220");
4135     n = SHOW_UTF8(n, "WACS_LLCORNER",   "\342\224\224");
4136     n = SHOW_UTF8(n, "WACS_LRCORNER",   "\342\224\230");
4137
4138     n = SHOW_UTF8(n, "WACS_LTEE",       "\342\224\234");
4139     n = SHOW_UTF8(n, "WACS_RTEE",       "\342\224\244");
4140     n = SHOW_UTF8(n, "WACS_TTEE",       "\342\224\254");
4141     n = SHOW_UTF8(n, "WACS_BTEE",       "\342\224\264");
4142
4143     n = SHOW_UTF8(n, "WACS_HLINE",      "\342\224\200");
4144     n = SHOW_UTF8(n, "WACS_VLINE",      "\342\224\202");
4145
4146     n = SHOW_UTF8(n, "WACS_LARROW",     "\342\206\220");
4147     n = SHOW_UTF8(n, "WACS_RARROW",     "\342\206\222");
4148     n = SHOW_UTF8(n, "WACS_UARROW",     "\342\206\221");
4149     n = SHOW_UTF8(n, "WACS_DARROW",     "\342\206\223");
4150
4151     n = SHOW_UTF8(n, "WACS_BLOCK",      "\342\226\256");
4152     n = SHOW_UTF8(n, "WACS_BOARD",      "\342\226\222");
4153     n = SHOW_UTF8(n, "WACS_LANTERN",    "\342\230\203");
4154     n = SHOW_UTF8(n, "WACS_BULLET",     "\302\267");
4155     n = SHOW_UTF8(n, "WACS_CKBOARD",    "\342\226\222");
4156     n = SHOW_UTF8(n, "WACS_DEGREE",     "\302\260");
4157     n = SHOW_UTF8(n, "WACS_DIAMOND",    "\342\227\206");
4158     n = SHOW_UTF8(n, "WACS_PLMINUS",    "\302\261");
4159     n = SHOW_UTF8(n, "WACS_PLUS",       "\342\224\274");
4160     n = SHOW_UTF8(n, "WACS_GEQUAL",     "\342\211\245");
4161     n = SHOW_UTF8(n, "WACS_NEQUAL",     "\342\211\240");
4162     n = SHOW_UTF8(n, "WACS_LEQUAL",     "\342\211\244");
4163
4164     n = SHOW_UTF8(n, "WACS_STERLING",   "\302\243");
4165     n = SHOW_UTF8(n, "WACS_PI",         "\317\200");
4166     n = SHOW_UTF8(n, "WACS_S1",         "\342\216\272");
4167     n = SHOW_UTF8(n, "WACS_S3",         "\342\216\273");
4168     n = SHOW_UTF8(n, "WACS_S7",         "\342\216\274");
4169     (void) SHOW_UTF8(n, "WACS_S9",      "\342\216\275");
4170     /* *INDENT-ON* */
4171
4172 }
4173
4174 /* display the wide-ACS character set */
4175 static void
4176 wide_acs_display(void)
4177 {
4178     int c = 'a';
4179     int digit = 0;
4180     int repeat = 1;
4181     int space = ' ';
4182     int pagesize = 32;
4183     chtype attr = A_NORMAL;
4184     int fg = COLOR_BLACK;
4185     int bg = COLOR_BLACK;
4186     unsigned at_code = 0;
4187     NCURSES_PAIRS_T pair = 0;
4188     void (*last_show_wacs) (int, attr_t, NCURSES_PAIRS_T) = 0;
4189     ATTR_TBL my_list[SIZEOF(attrs_to_test)];
4190     unsigned my_size = init_attr_list(my_list, term_attrs());
4191
4192     do {
4193         switch (c) {
4194         case CTRL('L'):
4195             Repaint();
4196             break;
4197         case 'a':
4198             ToggleAcs(last_show_wacs, show_wacs_chars);
4199             break;
4200 #ifdef WACS_D_PLUS
4201         case 'd':
4202             ToggleAcs(last_show_wacs, show_wacs_chars_double);
4203             break;
4204 #endif
4205 #ifdef WACS_T_PLUS
4206         case 't':
4207             ToggleAcs(last_show_wacs, show_wacs_chars_thick);
4208             break;
4209 #endif
4210         case 'w':
4211             if (pagesize == 32) {
4212                 pagesize = 256;
4213             } else {
4214                 pagesize = 32;
4215             }
4216             break;
4217         case 'x':
4218             ToggleAcs(last_show_wacs, show_wbox_chars);
4219             break;
4220         case 'u':
4221             ToggleAcs(last_show_wacs, show_utf8_chars);
4222             break;
4223         default:
4224             if (c < 256 && isdigit(c)) {
4225                 digit = (c - '0');
4226                 last_show_wacs = 0;
4227             } else if (c == '+') {
4228                 ++digit;
4229                 last_show_wacs = 0;
4230             } else if (c == '-' && digit > 0) {
4231                 --digit;
4232                 last_show_wacs = 0;
4233             } else if (c == '>' && repeat < (COLS / 4)) {
4234                 ++repeat;
4235             } else if (c == '<' && repeat > 1) {
4236                 --repeat;
4237             } else if (c == '_') {
4238                 space = (space == ' ') ? '_' : ' ';
4239                 last_show_wacs = 0;
4240             } else if (cycle_attr(c, &at_code, &attr, my_list, my_size)
4241                        || cycle_colors(c, &fg, &bg, &pair)) {
4242                 if (last_show_wacs != 0)
4243                     break;
4244             } else {
4245                 beep();
4246                 break;
4247             }
4248             break;
4249         }
4250         if (pagesize != 32) {
4251             show_paged_widechars(digit, pagesize, repeat, space, attr, pair);
4252         } else if (last_show_wacs != 0) {
4253             last_show_wacs(repeat, attr, pair);
4254         } else {
4255             show_upper_widechars(digit * 32 + 128, repeat, space, attr, pair);
4256         }
4257
4258         MvPrintw(LINES - 4, 0,
4259                  "Select: a/d/t WACS, w=all x=box, u UTF-8, ^L repaint");
4260         MvPrintw(LINES - 3, 2,
4261                  "0-9,+/- non-ASCII, </> repeat, _ space, ESC=quit");
4262         if (use_colors) {
4263             MvPrintw(LINES - 2, 2,
4264                      "v/V, f/F, b/B cycle through video attributes (%s) and color %d/%d.",
4265                      my_list[at_code].name,
4266                      fg, bg);
4267         } else {
4268             MvPrintw(LINES - 2, 2,
4269                      "v/V cycles through video attributes (%s).",
4270                      my_list[at_code].name);
4271         }
4272         refresh();
4273     } while (!isQuit(c = Getchar(), TRUE));
4274
4275     Pause();
4276     erase();
4277     endwin();
4278 }
4279
4280 #endif
4281
4282 /*
4283  * Graphic-rendition test (adapted from vttest)
4284  */
4285 static void
4286 test_sgr_attributes(void)
4287 {
4288     int pass;
4289
4290     for (pass = 0; pass < 2; pass++) {
4291         chtype normal = ((pass == 0 ? A_NORMAL : A_REVERSE)) | BLANK;
4292
4293         /* Use non-default colors if possible to exercise bce a little */
4294         if (use_colors) {
4295             init_pair(1, COLOR_WHITE, COLOR_BLUE);
4296             normal |= (chtype) COLOR_PAIR(1);
4297         }
4298         bkgdset(normal);
4299         erase();
4300         MvPrintw(1, 20, "Graphic rendition test pattern:");
4301
4302         MvPrintw(4, 1, "vanilla");
4303
4304 #define set_sgr(mask) bkgdset((normal^(mask)));
4305         set_sgr(A_BOLD);
4306         MvPrintw(4, 40, "bold");
4307
4308         set_sgr(A_UNDERLINE);
4309         MvPrintw(6, 6, "underline");
4310
4311         set_sgr(A_BOLD | A_UNDERLINE);
4312         MvPrintw(6, 45, "bold underline");
4313
4314         set_sgr(A_BLINK);
4315         MvPrintw(8, 1, "blink");
4316
4317         set_sgr(A_BLINK | A_BOLD);
4318         MvPrintw(8, 40, "bold blink");
4319
4320         set_sgr(A_UNDERLINE | A_BLINK);
4321         MvPrintw(10, 6, "underline blink");
4322
4323         set_sgr(A_BOLD | A_UNDERLINE | A_BLINK);
4324         MvPrintw(10, 45, "bold underline blink");
4325
4326         set_sgr(A_REVERSE);
4327         MvPrintw(12, 1, "negative");
4328
4329         set_sgr(A_BOLD | A_REVERSE);
4330         MvPrintw(12, 40, "bold negative");
4331
4332         set_sgr(A_UNDERLINE | A_REVERSE);
4333         MvPrintw(14, 6, "underline negative");
4334
4335         set_sgr(A_BOLD | A_UNDERLINE | A_REVERSE);
4336         MvPrintw(14, 45, "bold underline negative");
4337
4338         set_sgr(A_BLINK | A_REVERSE);
4339         MvPrintw(16, 1, "blink negative");
4340
4341         set_sgr(A_BOLD | A_BLINK | A_REVERSE);
4342         MvPrintw(16, 40, "bold blink negative");
4343
4344         set_sgr(A_UNDERLINE | A_BLINK | A_REVERSE);
4345         MvPrintw(18, 6, "underline blink negative");
4346
4347         set_sgr(A_BOLD | A_UNDERLINE | A_BLINK | A_REVERSE);
4348         MvPrintw(18, 45, "bold underline blink negative");
4349
4350         bkgdset(normal);
4351         MvPrintw(LINES - 2, 1, "%s background. ", pass == 0 ? "Dark" :
4352                  "Light");
4353         clrtoeol();
4354         Pause();
4355     }
4356
4357     bkgdset(A_NORMAL | BLANK);
4358     erase();
4359     endwin();
4360 }
4361
4362 /****************************************************************************
4363  *
4364  * Windows and scrolling tester.
4365  *
4366  ****************************************************************************/
4367
4368 #define BOTLINES        4       /* number of line stolen from screen bottom */
4369
4370 typedef struct {
4371     int y, x;
4372 } pair;
4373
4374 #define FRAME struct frame
4375 FRAME
4376 {
4377     FRAME *next, *last;
4378     bool do_scroll;
4379     bool do_keypad;
4380     WINDOW *wind;
4381 };
4382
4383 #if defined(NCURSES_VERSION) && NCURSES_EXT_FUNCS
4384 #if (NCURSES_VERSION_PATCH < 20070331)
4385 #define is_keypad(win)   (win)->_use_keypad
4386 #define is_scrollok(win) (win)->_scroll
4387 #endif
4388 #else
4389 #define is_keypad(win)   FALSE
4390 #define is_scrollok(win) FALSE
4391 #endif
4392
4393 static WINDOW *
4394 frame_win(FRAME * curp)
4395 {
4396     return (curp != 0) ? curp->wind : stdscr;
4397 }
4398
4399 /* We need to know if these flags are actually set, so don't look in FRAME.
4400  * These names are known to work with SVr4 curses as well as ncurses.  The
4401  * _use_keypad name does not work with Solaris 8.
4402  */
4403 static bool
4404 HaveKeypad(FRAME * curp)
4405 {
4406     WINDOW *win = frame_win(curp);
4407     (void) win;
4408     return is_keypad(win);
4409 }
4410
4411 static bool
4412 HaveScroll(FRAME * curp)
4413 {
4414     WINDOW *win = frame_win(curp);
4415     (void) win;
4416     return is_scrollok(win);
4417 }
4418
4419 static void
4420 newwin_legend(FRAME * curp)
4421 {
4422 #define DATA(num, name) { name, num }
4423     static const struct {
4424         const char *msg;
4425         int code;
4426     } legend[] = {
4427         DATA(0, "^C = create window"),
4428             DATA(0, "^N = next window"),
4429             DATA(0, "^P = previous window"),
4430             DATA(0, "^F = scroll forward"),
4431             DATA(0, "^B = scroll backward"),
4432             DATA(1, "^K = keypad(%s)"),
4433             DATA(2, "^S = scrollok(%s)"),
4434             DATA(0, "^W = save window"),
4435             DATA(0, "^R = restore window"),
4436 #if HAVE_WRESIZE
4437             DATA(0, "^X = resize"),
4438 #endif
4439             DATA(3, "^Q%s = exit")
4440     };
4441 #undef DATA
4442     size_t n;
4443     int x;
4444     bool do_keypad = HaveKeypad(curp);
4445     bool do_scroll = HaveScroll(curp);
4446     char buf[BUFSIZ];
4447
4448     move(LINES - 4, 0);
4449     for (n = 0; n < SIZEOF(legend); n++) {
4450         switch (legend[n].code) {
4451         default:
4452             _nc_STRCPY(buf, legend[n].msg, sizeof(buf));
4453             break;
4454         case 1:
4455             _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf))
4456                         legend[n].msg, do_keypad ? "yes" : "no");
4457             break;
4458         case 2:
4459             _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf))
4460                         legend[n].msg, do_scroll ? "yes" : "no");
4461             break;
4462         case 3:
4463             _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf))
4464                         legend[n].msg, do_keypad ? "/ESC" : "");
4465             break;
4466         }
4467         x = getcurx(stdscr);
4468         addstr((COLS < (x + 3 + (int) strlen(buf))) ? "\n" : (n ? ", " : ""));
4469         addstr(buf);
4470     }
4471     clrtoeol();
4472 }
4473
4474 static void
4475 transient(FRAME * curp, NCURSES_CONST char *msg)
4476 {
4477     newwin_legend(curp);
4478     if (msg) {
4479         MvAddStr(LINES - 1, 0, msg);
4480         refresh();
4481         napms(1000);
4482     }
4483
4484     move(LINES - 1, 0);
4485     printw("%s characters are echoed, window should %sscroll.",
4486            HaveKeypad(curp) ? "Non-arrow" : "All other",
4487            HaveScroll(curp) ? "" : "not ");
4488     clrtoeol();
4489 }
4490
4491 static void
4492 newwin_report(FRAME * curp)
4493 /* report on the cursor's current position, then restore it */
4494 {
4495     WINDOW *win = frame_win(curp);
4496     int y, x;
4497
4498     if (win != stdscr)
4499         transient(curp, (char *) 0);
4500     getyx(win, y, x);
4501     move(LINES - 1, COLS - 17);
4502     printw("Y = %2d X = %2d", y, x);
4503     if (win != stdscr)
4504         refresh();
4505     else
4506         wmove(win, y, x);
4507 }
4508
4509 static pair *
4510 selectcell(int uli, int ulj, int lri, int lrj)
4511 /* arrows keys move cursor, return location at current on non-arrow key */
4512 {
4513     static pair res;            /* result cell */
4514     int si = lri - uli + 1;     /* depth of the select area */
4515     int sj = lrj - ulj + 1;     /* width of the select area */
4516     int i = 0, j = 0;           /* offsets into the select area */
4517
4518     res.y = uli;
4519     res.x = ulj;
4520     for (;;) {
4521         move(uli + i, ulj + j);
4522         newwin_report((FRAME *) 0);
4523
4524         switch (Getchar()) {
4525         case KEY_UP:
4526             i += si - 1;
4527             break;
4528         case KEY_DOWN:
4529             i++;
4530             break;
4531         case KEY_LEFT:
4532             j += sj - 1;
4533             break;
4534         case KEY_RIGHT:
4535             j++;
4536             break;
4537         case case_QUIT:
4538             return ((pair *) 0);
4539 #ifdef NCURSES_MOUSE_VERSION
4540         case KEY_MOUSE:
4541             {
4542                 MEVENT event;
4543
4544                 getmouse(&event);
4545                 if (event.y > uli && event.x > ulj) {
4546                     i = event.y - uli;
4547                     j = event.x - ulj;
4548                 } else {
4549                     beep();
4550                     break;
4551                 }
4552             }
4553 #endif
4554             /* FALLTHRU */
4555         default:
4556             res.y = uli + i;
4557             res.x = ulj + j;
4558             return (&res);
4559         }
4560         i %= si;
4561         j %= sj;
4562     }
4563 }
4564
4565 static void
4566 outerbox(pair ul, pair lr, bool onoff)
4567 /* draw or erase a box *outside* the given pair of corners */
4568 {
4569     MvAddCh(ul.y - 1, lr.x - 1, onoff ? ACS_ULCORNER : ' ');
4570     MvAddCh(ul.y - 1, lr.x + 1, onoff ? ACS_URCORNER : ' ');
4571     MvAddCh(lr.y + 1, lr.x + 1, onoff ? ACS_LRCORNER : ' ');
4572     MvAddCh(lr.y + 1, ul.x - 1, onoff ? ACS_LLCORNER : ' ');
4573     move(ul.y - 1, ul.x);
4574     hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
4575     move(ul.y, ul.x - 1);
4576     vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
4577     move(lr.y + 1, ul.x);
4578     hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
4579     move(ul.y, lr.x + 1);
4580     vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
4581 }
4582
4583 static WINDOW *
4584 getwindow(void)
4585 /* Ask user for a window definition */
4586 {
4587     WINDOW *rwindow;
4588     pair ul, lr, *tmp;
4589
4590     move(0, 0);
4591     clrtoeol();
4592     addstr("Use arrows to move cursor, anything else to mark corner 1");
4593     refresh();
4594     if ((tmp = selectcell(2, 1, LINES - BOTLINES - 2, COLS - 2)) == (pair *) 0)
4595         return ((WINDOW *) 0);
4596     memcpy(&ul, tmp, sizeof(pair));
4597     MvAddCh(ul.y - 1, ul.x - 1, ACS_ULCORNER);
4598     move(0, 0);
4599     clrtoeol();
4600     addstr("Use arrows to move cursor, anything else to mark corner 2");
4601     refresh();
4602     if ((tmp = selectcell(ul.y, ul.x, LINES - BOTLINES - 2, COLS - 2)) ==
4603         (pair *) 0)
4604         return ((WINDOW *) 0);
4605     memcpy(&lr, tmp, sizeof(pair));
4606
4607     rwindow = subwin(stdscr, lr.y - ul.y + 1, lr.x - ul.x + 1, ul.y, ul.x);
4608
4609     outerbox(ul, lr, TRUE);
4610     refresh();
4611
4612     if (rwindow != 0)
4613         wrefresh(rwindow);
4614
4615     move(0, 0);
4616     clrtoeol();
4617     return (rwindow);
4618 }
4619
4620 static void
4621 newwin_move(FRAME * curp, int dy, int dx)
4622 {
4623     WINDOW *win = frame_win(curp);
4624     int cur_y, cur_x;
4625     int max_y, max_x;
4626
4627     getyx(win, cur_y, cur_x);
4628     getmaxyx(win, max_y, max_x);
4629     if ((cur_x += dx) < 0)
4630         cur_x = 0;
4631     else if (cur_x >= max_x)
4632         cur_x = max_x - 1;
4633     if ((cur_y += dy) < 0)
4634         cur_y = 0;
4635     else if (cur_y >= max_y)
4636         cur_y = max_y - 1;
4637     wmove(win, cur_y, cur_x);
4638 }
4639
4640 static FRAME *
4641 delete_framed(FRAME * fp, bool showit)
4642 {
4643     FRAME *np = 0;
4644
4645     if (fp != 0) {
4646         fp->last->next = fp->next;
4647         fp->next->last = fp->last;
4648
4649         if (showit) {
4650             werase(fp->wind);
4651             wrefresh(fp->wind);
4652         }
4653         delwin(fp->wind);
4654
4655         np = (fp == fp->next) ? NULL : fp->next;
4656         free(fp);
4657     }
4658     return np;
4659 }
4660
4661 static void
4662 acs_and_scroll(void)
4663 /* Demonstrate windows */
4664 {
4665     int c;
4666     FRAME *current = (FRAME *) 0, *neww;
4667     WINDOW *usescr;
4668 #if HAVE_PUTWIN && HAVE_GETWIN
4669     FILE *fp;
4670 #endif
4671
4672 #define DUMPFILE        "screendump"
4673
4674 #ifdef NCURSES_MOUSE_VERSION
4675     mousemask(BUTTON1_CLICKED, (mmask_t *) 0);
4676 #endif
4677     c = CTRL('C');
4678     raw();
4679     do {
4680         transient((FRAME *) 0, (char *) 0);
4681         switch (c) {
4682         case CTRL('C'):
4683             if ((neww = typeCalloc(FRAME, (size_t) 1)) == 0) {
4684                 failed("acs_and_scroll");
4685                 goto breakout;
4686             }
4687             if ((neww->wind = getwindow()) == (WINDOW *) 0) {
4688                 failed("acs_and_scroll");
4689                 free(neww);
4690                 goto breakout;
4691             }
4692
4693             if (current == 0) { /* First element,  */
4694                 neww->next = neww;      /*   so point it at itself */
4695                 neww->last = neww;
4696             } else {
4697                 neww->next = current->next;
4698                 neww->last = current;
4699                 neww->last->next = neww;
4700                 neww->next->last = neww;
4701             }
4702             current = neww;
4703             /* SVr4 curses sets the keypad on all newly-created windows to
4704              * false.  Someone reported that PDCurses makes new windows inherit
4705              * this flag.  Remove the following 'keypad()' call to test this
4706              */
4707             keypad(current->wind, TRUE);
4708             current->do_keypad = HaveKeypad(current);
4709             current->do_scroll = HaveScroll(current);
4710             break;
4711
4712         case CTRL('N'): /* go to next window */
4713             if (current)
4714                 current = current->next;
4715             break;
4716
4717         case CTRL('P'): /* go to previous window */
4718             if (current)
4719                 current = current->last;
4720             break;
4721
4722         case CTRL('F'): /* scroll current window forward */
4723             if (current)
4724                 wscrl(frame_win(current), 1);
4725             break;
4726
4727         case CTRL('B'): /* scroll current window backwards */
4728             if (current)
4729                 wscrl(frame_win(current), -1);
4730             break;
4731
4732         case CTRL('K'): /* toggle keypad mode for current */
4733             if (current) {
4734                 current->do_keypad = !current->do_keypad;
4735                 keypad(current->wind, current->do_keypad);
4736             }
4737             break;
4738
4739         case CTRL('S'):
4740             if (current) {
4741                 current->do_scroll = !current->do_scroll;
4742                 scrollok(current->wind, current->do_scroll);
4743             }
4744             break;
4745
4746 #if HAVE_PUTWIN && HAVE_GETWIN
4747         case CTRL('W'): /* save and delete window */
4748             if ((current != 0) && (current == current->next)) {
4749                 transient(current, "Will not save/delete ONLY window");
4750                 break;
4751             } else if ((fp = fopen(DUMPFILE, "w")) == (FILE *) 0) {
4752                 transient(current, "Can't open screen dump file");
4753             } else {
4754                 int rc = putwin(frame_win(current), fp);
4755                 (void) fclose(fp);
4756
4757                 if (rc == OK) {
4758                     current = delete_framed(current, TRUE);
4759                 } else {
4760                     transient(current, "Can't write screen dump file");
4761                 }
4762             }
4763             break;
4764
4765         case CTRL('R'): /* restore window */
4766             if ((fp = fopen(DUMPFILE, "r")) == (FILE *) 0) {
4767                 transient(current, "Can't open screen dump file");
4768             } else {
4769                 if ((neww = typeCalloc(FRAME, (size_t) 1)) != 0) {
4770
4771                     neww->next = current ? current->next : 0;
4772                     neww->last = current;
4773                     if (neww->last != 0)
4774                         neww->last->next = neww;
4775                     if (neww->next != 0)
4776                         neww->next->last = neww;
4777
4778                     neww->wind = getwin(fp);
4779
4780                     wrefresh(neww->wind);
4781                 } else {
4782                     failed("acs_and_scroll");
4783                 }
4784                 (void) fclose(fp);
4785             }
4786             break;
4787 #endif
4788
4789 #if HAVE_WRESIZE
4790         case CTRL('X'): /* resize window */
4791             if (current) {
4792                 pair *tmp, ul, lr;
4793                 int i, mx, my;
4794
4795                 move(0, 0);
4796                 clrtoeol();
4797                 addstr("Use arrows to move cursor, anything else to mark new corner");
4798                 refresh();
4799
4800                 getbegyx(current->wind, ul.y, ul.x);
4801
4802                 tmp = selectcell(ul.y, ul.x, LINES - BOTLINES - 2, COLS - 2);
4803                 if (tmp == (pair *) 0) {
4804                     beep();
4805                     break;
4806                 }
4807
4808                 getmaxyx(current->wind, lr.y, lr.x);
4809                 lr.y += (ul.y - 1);
4810                 lr.x += (ul.x - 1);
4811                 outerbox(ul, lr, FALSE);
4812                 wnoutrefresh(stdscr);
4813
4814                 /* strictly cosmetic hack for the test */
4815                 getmaxyx(current->wind, my, mx);
4816                 if (my > tmp->y - ul.y) {
4817                     getyx(current->wind, lr.y, lr.x);
4818                     wmove(current->wind, tmp->y - ul.y + 1, 0);
4819                     wclrtobot(current->wind);
4820                     wmove(current->wind, lr.y, lr.x);
4821                 }
4822                 if (mx > tmp->x - ul.x)
4823                     for (i = 0; i < my; i++) {
4824                         wmove(current->wind, i, tmp->x - ul.x + 1);
4825                         wclrtoeol(current->wind);
4826                     }
4827                 wnoutrefresh(current->wind);
4828
4829                 memcpy(&lr, tmp, sizeof(pair));
4830                 (void) wresize(current->wind, lr.y - ul.y + 0, lr.x - ul.x + 0);
4831
4832                 getbegyx(current->wind, ul.y, ul.x);
4833                 getmaxyx(current->wind, lr.y, lr.x);
4834                 lr.y += (ul.y - 1);
4835                 lr.x += (ul.x - 1);
4836                 outerbox(ul, lr, TRUE);
4837                 wnoutrefresh(stdscr);
4838
4839                 wnoutrefresh(current->wind);
4840                 move(0, 0);
4841                 clrtoeol();
4842                 doupdate();
4843             }
4844             break;
4845 #endif /* HAVE_WRESIZE */
4846
4847         case KEY_UP:
4848             newwin_move(current, -1, 0);
4849             break;
4850         case KEY_DOWN:
4851             newwin_move(current, 1, 0);
4852             break;
4853         case KEY_LEFT:
4854             newwin_move(current, 0, -1);
4855             break;
4856         case KEY_RIGHT:
4857             newwin_move(current, 0, 1);
4858             break;
4859
4860         case KEY_BACKSPACE:
4861             /* FALLTHROUGH */
4862         case KEY_DC:
4863             {
4864                 int y, x;
4865                 getyx(frame_win(current), y, x);
4866                 if (--x < 0) {
4867                     if (--y < 0)
4868                         break;
4869                     x = getmaxx(frame_win(current)) - 1;
4870                 }
4871                 (void) mvwdelch(frame_win(current), y, x);
4872             }
4873             break;
4874
4875         case '\r':
4876             c = '\n';
4877             /* FALLTHROUGH */
4878
4879         default:
4880             if (current)
4881                 waddch(current->wind, (chtype) c);
4882             else
4883                 beep();
4884             break;
4885         }
4886         newwin_report(current);
4887         usescr = frame_win(current);
4888         wrefresh(usescr);
4889     } while
4890         (!isQuit(c = wGetchar(usescr), TRUE)
4891          && (c != ERR));
4892
4893   breakout:
4894     while (current != 0)
4895         current = delete_framed(current, FALSE);
4896
4897     scrollok(stdscr, TRUE);     /* reset to driver's default */
4898 #ifdef NCURSES_MOUSE_VERSION
4899     mousemask(0, (mmask_t *) 0);
4900 #endif
4901     noraw();
4902     erase();
4903     endwin();
4904 }
4905
4906 /****************************************************************************
4907  *
4908  * Panels tester
4909  *
4910  ****************************************************************************/
4911
4912 #if USE_LIBPANEL
4913 static int nap_msec = 1;
4914
4915 static NCURSES_CONST char *mod[] =
4916 {
4917     "test ",
4918     "TEST ",
4919     "(**) ",
4920     "*()* ",
4921     "<--> ",
4922     "LAST "
4923 };
4924
4925 /*+-------------------------------------------------------------------------
4926         wait_a_while(msec)
4927 --------------------------------------------------------------------------*/
4928 static void
4929 wait_a_while(int msec GCC_UNUSED)
4930 {
4931 #if HAVE_NAPMS
4932     if (nap_msec == 1)
4933         wGetchar(stdscr);
4934     else
4935         napms(nap_msec);
4936 #else
4937     if (nap_msec == 1)
4938         wGetchar(stdscr);
4939     else if (msec > 1000)
4940         sleep((unsigned) msec / 1000);
4941     else
4942         sleep(1);
4943 #endif
4944 }                               /* end of wait_a_while */
4945
4946 /*+-------------------------------------------------------------------------
4947         saywhat(text)
4948 --------------------------------------------------------------------------*/
4949 static void
4950 saywhat(NCURSES_CONST char *text)
4951 {
4952     wmove(stdscr, LINES - 1, 0);
4953     wclrtoeol(stdscr);
4954     if (text != 0 && *text != '\0') {
4955         waddstr(stdscr, text);
4956         waddstr(stdscr, "; ");
4957     }
4958     waddstr(stdscr, "press any key to continue");
4959 }                               /* end of saywhat */
4960
4961 /*+-------------------------------------------------------------------------
4962         mkpanel(rows,cols,tly,tlx) - alloc a win and panel and associate them
4963 --------------------------------------------------------------------------*/
4964 static PANEL *
4965 mkpanel(NCURSES_COLOR_T color, int rows, int cols, int tly, int tlx)
4966 {
4967     WINDOW *win;
4968     PANEL *pan = 0;
4969
4970     if ((win = newwin(rows, cols, tly, tlx)) != 0) {
4971         if ((pan = new_panel(win)) == 0) {
4972             delwin(win);
4973         } else if (use_colors) {
4974             NCURSES_COLOR_T fg = (NCURSES_COLOR_T) ((color == COLOR_BLUE)
4975                                                     ? COLOR_WHITE
4976                                                     : COLOR_BLACK);
4977             NCURSES_COLOR_T bg = color;
4978
4979             init_pair(color, fg, bg);
4980             wbkgdset(win, (attr_t) (COLOR_PAIR(color) | ' '));
4981         } else {
4982             wbkgdset(win, A_BOLD | ' ');
4983         }
4984     }
4985     return pan;
4986 }                               /* end of mkpanel */
4987
4988 /*+-------------------------------------------------------------------------
4989         rmpanel(pan)
4990 --------------------------------------------------------------------------*/
4991 static void
4992 rmpanel(PANEL *pan)
4993 {
4994     WINDOW *win = panel_window(pan);
4995     del_panel(pan);
4996     delwin(win);
4997 }                               /* end of rmpanel */
4998
4999 /*+-------------------------------------------------------------------------
5000         pflush()
5001 --------------------------------------------------------------------------*/
5002 static void
5003 pflush(void)
5004 {
5005     update_panels();
5006     doupdate();
5007 }                               /* end of pflush */
5008
5009 /*+-------------------------------------------------------------------------
5010         fill_panel(win)
5011 --------------------------------------------------------------------------*/
5012 static void
5013 init_panel(WINDOW *win)
5014 {
5015     register int y, x;
5016
5017     for (y = 0; y < LINES - 1; y++) {
5018         for (x = 0; x < COLS; x++)
5019             wprintw(win, "%d", (y + x) % 10);
5020     }
5021 }
5022
5023 static void
5024 fill_panel(PANEL *pan)
5025 {
5026     WINDOW *win = panel_window(pan);
5027     const char *userptr = (const char *) panel_userptr(pan);
5028     int num = (userptr && *userptr) ? userptr[1] : '?';
5029     int y, x;
5030
5031     wmove(win, 1, 1);
5032     wprintw(win, "-pan%c-", num);
5033     wclrtoeol(win);
5034     box(win, 0, 0);
5035     for (y = 2; y < getmaxy(win) - 1; y++) {
5036         for (x = 1; x < getmaxx(win) - 1; x++) {
5037             wmove(win, y, x);
5038             waddch(win, UChar(num));
5039         }
5040     }
5041 }
5042
5043 #if USE_WIDEC_SUPPORT
5044 static void
5045 init_wide_panel(WINDOW *win)
5046 {
5047     int digit;
5048     cchar_t temp[10];
5049
5050     for (digit = 0; digit < 10; ++digit)
5051         make_fullwidth_digit(&temp[digit], digit);
5052
5053     do {
5054         int y, x;
5055         getyx(stdscr, y, x);
5056         digit = (y + x / 2) % 10;
5057     } while (wadd_wch(win, &temp[digit]) != ERR);
5058 }
5059
5060 static void
5061 fill_wide_panel(PANEL *pan)
5062 {
5063     WINDOW *win = panel_window(pan);
5064     const char *userptr = (const char *) panel_userptr(pan);
5065     int num = (userptr && *userptr) ? userptr[1] : '?';
5066     int y, x;
5067
5068     wmove(win, 1, 1);
5069     wprintw(win, "-pan%c-", num);
5070     wclrtoeol(win);
5071     box(win, 0, 0);
5072     for (y = 2; y < getmaxy(win) - 1; y++) {
5073         for (x = 1; x < getmaxx(win) - 1; x++) {
5074             wmove(win, y, x);
5075             waddch(win, UChar(num));
5076         }
5077     }
5078 }
5079 #endif
5080
5081 #define MAX_PANELS 5
5082
5083 static void
5084 canned_panel(PANEL *px[MAX_PANELS + 1], NCURSES_CONST char *cmd)
5085 {
5086     int which = cmd[1] - '0';
5087
5088     saywhat(cmd);
5089     switch (*cmd) {
5090     case 'h':
5091         hide_panel(px[which]);
5092         break;
5093     case 's':
5094         show_panel(px[which]);
5095         break;
5096     case 't':
5097         top_panel(px[which]);
5098         break;
5099     case 'b':
5100         bottom_panel(px[which]);
5101         break;
5102     case 'd':
5103         rmpanel(px[which]);
5104         break;
5105     }
5106     pflush();
5107     wait_a_while(nap_msec);
5108 }
5109
5110 static void
5111 demo_panels(void (*InitPanel) (WINDOW *), void (*FillPanel) (PANEL *))
5112 {
5113     int count;
5114     int itmp;
5115     PANEL *px[MAX_PANELS + 1];
5116
5117     scrollok(stdscr, FALSE);    /* we don't want stdscr to scroll! */
5118     refresh();
5119
5120     InitPanel(stdscr);
5121     for (count = 0; count < 5; count++) {
5122         px[1] = mkpanel(COLOR_RED,
5123                         LINES / 2 - 2,
5124                         COLS / 8 + 1,
5125                         0,
5126                         0);
5127         set_panel_userptr(px[1], (NCURSES_CONST void *) "p1");
5128
5129         px[2] = mkpanel(COLOR_GREEN,
5130                         LINES / 2 + 1,
5131                         COLS / 7,
5132                         LINES / 4,
5133                         COLS / 10);
5134         set_panel_userptr(px[2], (NCURSES_CONST void *) "p2");
5135
5136         px[3] = mkpanel(COLOR_YELLOW,
5137                         LINES / 4,
5138                         COLS / 10,
5139                         LINES / 2,
5140                         COLS / 9);
5141         set_panel_userptr(px[3], (NCURSES_CONST void *) "p3");
5142
5143         px[4] = mkpanel(COLOR_BLUE,
5144                         LINES / 2 - 2,
5145                         COLS / 8,
5146                         LINES / 2 - 2,
5147                         COLS / 3);
5148         set_panel_userptr(px[4], (NCURSES_CONST void *) "p4");
5149
5150         px[5] = mkpanel(COLOR_MAGENTA,
5151                         LINES / 2 - 2,
5152                         COLS / 8,
5153                         LINES / 2,
5154                         COLS / 2 - 2);
5155         set_panel_userptr(px[5], (NCURSES_CONST void *) "p5");
5156
5157         FillPanel(px[1]);
5158         FillPanel(px[2]);
5159         FillPanel(px[3]);
5160         FillPanel(px[4]);
5161         FillPanel(px[5]);
5162
5163         hide_panel(px[4]);
5164         hide_panel(px[5]);
5165         pflush();
5166         saywhat("");
5167         wait_a_while(nap_msec);
5168
5169         saywhat("h3 s1 s2 s4 s5");
5170         move_panel(px[1], 0, 0);
5171         hide_panel(px[3]);
5172         show_panel(px[1]);
5173         show_panel(px[2]);
5174         show_panel(px[4]);
5175         show_panel(px[5]);
5176         pflush();
5177         wait_a_while(nap_msec);
5178
5179         canned_panel(px, "s1");
5180         canned_panel(px, "s2");
5181
5182         saywhat("m2");
5183         move_panel(px[2], LINES / 3 + 1, COLS / 8);
5184         pflush();
5185         wait_a_while(nap_msec);
5186
5187         canned_panel(px, "s3");
5188
5189         saywhat("m3");
5190         move_panel(px[3], LINES / 4 + 1, COLS / 15);
5191         pflush();
5192         wait_a_while(nap_msec);
5193
5194         canned_panel(px, "b3");
5195         canned_panel(px, "s4");
5196         canned_panel(px, "s5");
5197         canned_panel(px, "t3");
5198         canned_panel(px, "t1");
5199         canned_panel(px, "t2");
5200         canned_panel(px, "t3");
5201         canned_panel(px, "t4");
5202
5203         for (itmp = 0; itmp < 6; itmp++) {
5204             WINDOW *w4 = panel_window(px[4]);
5205             WINDOW *w5 = panel_window(px[5]);
5206
5207             saywhat("m4");
5208             wmove(w4, LINES / 8, 1);
5209             waddstr(w4, mod[itmp]);
5210             move_panel(px[4], LINES / 6, itmp * (COLS / 8));
5211             wmove(w5, LINES / 6, 1);
5212             waddstr(w5, mod[itmp]);
5213             pflush();
5214             wait_a_while(nap_msec);
5215
5216             saywhat("m5");
5217             wmove(w4, LINES / 6, 1);
5218             waddstr(w4, mod[itmp]);
5219             move_panel(px[5], LINES / 3 - 1, (itmp * 10) + 6);
5220             wmove(w5, LINES / 8, 1);
5221             waddstr(w5, mod[itmp]);
5222             pflush();
5223             wait_a_while(nap_msec);
5224         }
5225
5226         saywhat("m4");
5227         move_panel(px[4], LINES / 6, itmp * (COLS / 8));
5228         pflush();
5229         wait_a_while(nap_msec);
5230
5231         canned_panel(px, "t5");
5232         canned_panel(px, "t2");
5233         canned_panel(px, "t1");
5234         canned_panel(px, "d2");
5235         canned_panel(px, "h3");
5236         canned_panel(px, "d1");
5237         canned_panel(px, "d4");
5238         canned_panel(px, "d5");
5239         canned_panel(px, "d3");
5240
5241         wait_a_while(nap_msec);
5242         if (nap_msec == 1)
5243             break;
5244         nap_msec = 100L;
5245     }
5246
5247     erase();
5248     endwin();
5249 }
5250 #endif /* USE_LIBPANEL */
5251
5252 /****************************************************************************
5253  *
5254  * Pad tester
5255  *
5256  ****************************************************************************/
5257
5258 #define GRIDSIZE        3
5259
5260 static bool pending_pan = FALSE;
5261 static bool show_panner_legend = TRUE;
5262
5263 static int
5264 panner_legend(int line)
5265 {
5266     static const char *const legend[] =
5267     {
5268         "Use arrow keys (or U,D,L,R) to pan, ESC to quit, ! to shell-out.",
5269         "Use +,- (or j,k) to grow/shrink the panner vertically.",
5270         "Use <,> (or h,l) to grow/shrink the panner horizontally.",
5271         "Number repeats.  Toggle legend:? filler:a timer:t scrollmark:s."
5272     };
5273     int n = ((int) SIZEOF(legend) - (LINES - line));
5274     if (n >= 0) {
5275         if (move(line, 0) != ERR) {
5276             if (show_panner_legend)
5277                 printw("%s", legend[n]);
5278             clrtoeol();
5279             return show_panner_legend;
5280         }
5281     }
5282     return FALSE;
5283 }
5284
5285 static void
5286 panner_h_cleanup(int from_y, int from_x, int to_x)
5287 {
5288     if (!panner_legend(from_y))
5289         do_h_line(from_y, from_x, ' ', to_x);
5290 }
5291
5292 static void
5293 panner_v_cleanup(int from_y, int from_x, int to_y)
5294 {
5295     if (!panner_legend(from_y))
5296         do_v_line(from_y, from_x, ' ', to_y);
5297 }
5298
5299 static void
5300 fill_pad(WINDOW *panpad, bool pan_lines, bool colored)
5301 {
5302     int y, x;
5303     unsigned gridcount = 0;
5304     chtype fill = 0;
5305 #ifdef A_COLOR
5306     if (colored)
5307         fill = (chtype) COLOR_PAIR(1);
5308 #endif
5309
5310     wmove(panpad, 0, 0);
5311     for (y = 0; y < getmaxy(panpad); y++) {
5312         for (x = 0; x < getmaxx(panpad); x++) {
5313             if (y % GRIDSIZE == 0 && x % GRIDSIZE == 0) {
5314                 if (y == 0 && x == 0)
5315                     waddch(panpad, pan_lines ? ACS_ULCORNER : '+');
5316                 else if (y == 0)
5317                     waddch(panpad, pan_lines ? ACS_TTEE : '+');
5318                 else if (y == 0 || x == 0)
5319                     waddch(panpad, pan_lines ? ACS_LTEE : '+');
5320                 else
5321                     waddch(panpad, (chtype) ((pan_lines ? 'a' : 'A') +
5322                                              (int) (gridcount++ % 26)) | fill);
5323             } else if (y % GRIDSIZE == 0)
5324                 waddch(panpad, pan_lines ? ACS_HLINE : '-');
5325             else if (x % GRIDSIZE == 0)
5326                 waddch(panpad, pan_lines ? ACS_VLINE : '|');
5327             else
5328                 waddch(panpad, ' ');
5329         }
5330     }
5331 }
5332
5333 static void
5334 panner(WINDOW *pad,
5335        int top_x, int top_y, int porty, int portx,
5336        int (*pgetc) (WINDOW *),
5337        bool colored)
5338 {
5339 #if HAVE_GETTIMEOFDAY
5340     struct timeval before, after;
5341     bool timing = TRUE;
5342 #endif
5343     bool pan_lines = FALSE;
5344     bool scrollers = TRUE;
5345     int basex = 0;
5346     int basey = 0;
5347     int pxmax, pymax, lowend, highend, c;
5348
5349     getmaxyx(pad, pymax, pxmax);
5350     scrollok(stdscr, FALSE);    /* we don't want stdscr to scroll! */
5351
5352     c = KEY_REFRESH;
5353     do {
5354 #ifdef NCURSES_VERSION
5355         /*
5356          * During shell-out, the user may have resized the window.  Adjust
5357          * the port size of the pad to accommodate this.  Ncurses automatically
5358          * resizes all of the normal windows to fit on the new screen.
5359          */
5360         if (top_x > COLS)
5361             top_x = COLS;
5362         if (portx > COLS)
5363             portx = COLS;
5364         if (top_y > LINES)
5365             top_y = LINES;
5366         if (porty > LINES)
5367             porty = LINES;
5368 #endif
5369         switch (c) {
5370         case KEY_REFRESH:
5371             erase();
5372
5373             /* FALLTHRU */
5374         case HELP_KEY_1:
5375             if (c == HELP_KEY_1)
5376                 show_panner_legend = !show_panner_legend;
5377             panner_legend(LINES - 4);
5378             panner_legend(LINES - 3);
5379             panner_legend(LINES - 2);
5380             panner_legend(LINES - 1);
5381             break;
5382         case 'a':
5383             pan_lines = !pan_lines;
5384             fill_pad(pad, pan_lines, colored);
5385             pending_pan = FALSE;
5386             break;
5387
5388 #if HAVE_GETTIMEOFDAY
5389         case 't':
5390             timing = !timing;
5391             if (!timing)
5392                 panner_legend(LINES - 1);
5393             break;
5394 #endif
5395         case 's':
5396             scrollers = !scrollers;
5397             break;
5398
5399             /* Move the top-left corner of the pad, keeping the bottom-right
5400              * corner fixed.
5401              */
5402         case 'h':               /* increase-columns: move left edge to left */
5403             if (top_x <= 0)
5404                 beep();
5405             else {
5406                 panner_v_cleanup(top_y, top_x, porty);
5407                 top_x--;
5408             }
5409             break;
5410
5411         case 'j':               /* decrease-lines: move top-edge down */
5412             if (top_y >= porty)
5413                 beep();
5414             else {
5415                 panner_h_cleanup(top_y - 1, top_x - (top_x > 0), portx);
5416                 top_y++;
5417             }
5418             break;
5419
5420         case 'k':               /* increase-lines: move top-edge up */
5421             if (top_y <= 0)
5422                 beep();
5423             else {
5424                 top_y--;
5425                 panner_h_cleanup(top_y, top_x, portx);
5426             }
5427             break;
5428
5429         case 'l':               /* decrease-columns: move left-edge to right */
5430             if (top_x >= portx)
5431                 beep();
5432             else {
5433                 panner_v_cleanup(top_y - (top_y > 0), top_x - 1, porty);
5434                 top_x++;
5435             }
5436             break;
5437
5438             /* Move the bottom-right corner of the pad, keeping the top-left
5439              * corner fixed.
5440              */
5441         case KEY_IC:            /* increase-columns: move right-edge to right */
5442             if (portx >= pxmax || portx >= COLS)
5443                 beep();
5444             else {
5445                 panner_v_cleanup(top_y - (top_y > 0), portx - 1, porty);
5446                 ++portx;
5447             }
5448             break;
5449
5450         case KEY_IL:            /* increase-lines: move bottom-edge down */
5451             if (porty >= pymax || porty >= LINES)
5452                 beep();
5453             else {
5454                 panner_h_cleanup(porty - 1, top_x - (top_x > 0), portx);
5455                 ++porty;
5456             }
5457             break;
5458
5459         case KEY_DC:            /* decrease-columns: move bottom edge up */
5460             if (portx <= top_x)
5461                 beep();
5462             else {
5463                 portx--;
5464                 panner_v_cleanup(top_y - (top_y > 0), portx, porty);
5465             }
5466             break;
5467
5468         case KEY_DL:            /* decrease-lines */
5469             if (porty <= top_y)
5470                 beep();
5471             else {
5472                 porty--;
5473                 panner_h_cleanup(porty, top_x - (top_x > 0), portx);
5474             }
5475             break;
5476
5477         case KEY_LEFT:          /* pan leftwards */
5478             if (basex > 0)
5479                 basex--;
5480             else
5481                 beep();
5482             break;
5483
5484         case KEY_RIGHT: /* pan rightwards */
5485             if (basex + portx - (pymax > porty) < pxmax)
5486                 basex++;
5487             else
5488                 beep();
5489             break;
5490
5491         case KEY_UP:            /* pan upwards */
5492             if (basey > 0)
5493                 basey--;
5494             else
5495                 beep();
5496             break;
5497
5498         case KEY_DOWN:          /* pan downwards */
5499             if (basey + porty - (pxmax > portx) < pymax)
5500                 basey++;
5501             else
5502                 beep();
5503             break;
5504
5505         case 'H':
5506         case KEY_HOME:
5507         case KEY_FIND:
5508             basey = 0;
5509             break;
5510
5511         case 'E':
5512         case KEY_END:
5513         case KEY_SELECT:
5514             basey = pymax - porty;
5515             if (basey < 0)
5516                 basey = 0;
5517             break;
5518
5519         default:
5520             beep();
5521             break;
5522         }
5523
5524         MvAddCh(top_y - 1, top_x - 1, ACS_ULCORNER);
5525         do_v_line(top_y, top_x - 1, ACS_VLINE, porty);
5526         do_h_line(top_y - 1, top_x, ACS_HLINE, portx);
5527
5528         if (scrollers && (pxmax > portx - 1)) {
5529             int length = (portx - top_x - 1);
5530             float ratio = ((float) length) / ((float) pxmax);
5531
5532             lowend = (int) ((float) top_x + ((float) basex * ratio));
5533             highend = (int) ((float) top_x + ((float) (basex + length) * ratio));
5534
5535             do_h_line(porty - 1, top_x, ACS_HLINE, lowend);
5536             if (highend < portx) {
5537                 attron(A_REVERSE);
5538                 do_h_line(porty - 1, lowend, ' ', highend + 1);
5539                 attroff(A_REVERSE);
5540                 do_h_line(porty - 1, highend + 1, ACS_HLINE, portx);
5541             }
5542         } else
5543             do_h_line(porty - 1, top_x, ACS_HLINE, portx);
5544
5545         if (scrollers && (pymax > porty - 1)) {
5546             int length = (porty - top_y - 1);
5547             float ratio = ((float) length) / ((float) pymax);
5548
5549             lowend = (int) ((float) top_y + ((float) basey * ratio));
5550             highend = (int) ((float) top_y + ((float) (basey + length) * ratio));
5551
5552             do_v_line(top_y, portx - 1, ACS_VLINE, lowend);
5553             if (highend < porty) {
5554                 attron(A_REVERSE);
5555                 do_v_line(lowend, portx - 1, ' ', highend + 1);
5556                 attroff(A_REVERSE);
5557                 do_v_line(highend + 1, portx - 1, ACS_VLINE, porty);
5558             }
5559         } else
5560             do_v_line(top_y, portx - 1, ACS_VLINE, porty);
5561
5562         MvAddCh(top_y - 1, portx - 1, ACS_URCORNER);
5563         MvAddCh(porty - 1, top_x - 1, ACS_LLCORNER);
5564         MvAddCh(porty - 1, portx - 1, ACS_LRCORNER);
5565
5566         if (!pending_pan) {
5567 #if HAVE_GETTIMEOFDAY
5568             gettimeofday(&before, 0);
5569 #endif
5570             wnoutrefresh(stdscr);
5571
5572             pnoutrefresh(pad,
5573                          basey, basex,
5574                          top_y, top_x,
5575                          porty - (pxmax > portx) - 1,
5576                          portx - (pymax > porty) - 1);
5577
5578             doupdate();
5579 #if HAVE_GETTIMEOFDAY
5580 #define TIMEVAL2S(data) ((double) data.tv_sec + ((double) data.tv_usec / 1.0e6))
5581             if (timing) {
5582                 double elapsed;
5583                 gettimeofday(&after, 0);
5584                 elapsed = (TIMEVAL2S(after) - TIMEVAL2S(before));
5585                 move(LINES - 1, COLS - 12);
5586                 printw("Secs: %2.03f", elapsed);
5587                 refresh();
5588             }
5589 #endif
5590         }
5591
5592     } while
5593         ((c = pgetc(pad)) != KEY_EXIT);
5594
5595     scrollok(stdscr, TRUE);     /* reset to driver's default */
5596 }
5597
5598 static int
5599 padgetch(WINDOW *win)
5600 {
5601     static int count;
5602     static int last;
5603     int c;
5604
5605     if ((pending_pan = (count > 0)) != FALSE) {
5606         count--;
5607         pending_pan = (count != 0);
5608     } else {
5609         for (;;) {
5610             switch (c = wGetchar(win)) {
5611             case '!':
5612                 ShellOut(FALSE);
5613                 /* FALLTHRU */
5614             case CTRL('r'):
5615                 endwin();
5616                 refresh();
5617                 c = KEY_REFRESH;
5618                 break;
5619             case CTRL('l'):
5620                 c = KEY_REFRESH;
5621                 break;
5622             case 'U':
5623                 c = KEY_UP;
5624                 break;
5625             case 'D':
5626                 c = KEY_DOWN;
5627                 break;
5628             case 'R':
5629                 c = KEY_RIGHT;
5630                 break;
5631             case 'L':
5632                 c = KEY_LEFT;
5633                 break;
5634             case '+':
5635                 c = KEY_IL;
5636                 break;
5637             case '-':
5638                 c = KEY_DL;
5639                 break;
5640             case '>':
5641                 c = KEY_IC;
5642                 break;
5643             case '<':
5644                 c = KEY_DC;
5645                 break;
5646             case ERR:           /* FALLTHRU */
5647             case case_QUIT:
5648                 count = 0;
5649                 c = KEY_EXIT;
5650                 break;
5651             default:
5652                 if (c >= '0' && c <= '9') {
5653                     count = count * 10 + (c - '0');
5654                     continue;
5655                 }
5656                 break;
5657             }
5658             last = c;
5659             break;
5660         }
5661         if (count > 0)
5662             count--;
5663     }
5664     return (last);
5665 }
5666
5667 #define PAD_HIGH 200
5668 #define PAD_WIDE 200
5669
5670 static void
5671 demo_pad(bool colored)
5672 /* Demonstrate pads. */
5673 {
5674     WINDOW *panpad = newpad(PAD_HIGH, PAD_WIDE);
5675
5676     if (panpad == 0) {
5677         Cannot("cannot create requested pad");
5678         return;
5679     }
5680 #ifdef A_COLOR
5681     if (colored && use_colors) {
5682         init_pair(1, COLOR_BLACK, COLOR_GREEN);
5683         init_pair(2, COLOR_CYAN, COLOR_BLUE);
5684         wbkgd(panpad, (chtype) (COLOR_PAIR(2) | ' '));
5685     }
5686 #endif
5687     fill_pad(panpad, FALSE, colored);
5688
5689     panner_legend(LINES - 4);
5690     panner_legend(LINES - 3);
5691     panner_legend(LINES - 2);
5692     panner_legend(LINES - 1);
5693
5694     keypad(panpad, TRUE);
5695
5696     /* Make the pad (initially) narrow enough that a trace file won't wrap.
5697      * We'll still be able to widen it during a test, since that's required
5698      * for testing boundaries.
5699      */
5700     panner(panpad, 2, 2, LINES - 5, COLS - 15, padgetch, colored);
5701
5702     delwin(panpad);
5703     endwin();
5704     erase();
5705 }
5706
5707 /****************************************************************************
5708  *
5709  * Tests from John Burnell's PDCurses tester
5710  *
5711  ****************************************************************************/
5712
5713 static void
5714 Continue(WINDOW *win)
5715 {
5716     noecho();
5717     wmove(win, 10, 1);
5718     MvWAddStr(win, 10, 1, " Press any key to continue");
5719     wrefresh(win);
5720     wGetchar(win);
5721 }
5722
5723 static void
5724 flushinp_test(WINDOW *win)
5725 /* Input test, adapted from John Burnell's PDCurses tester */
5726 {
5727     int w, h, bx, by, sw, sh, i;
5728
5729     WINDOW *subWin;
5730     wclear(win);
5731
5732     getmaxyx(win, h, w);
5733     getbegyx(win, by, bx);
5734     sw = w / 3;
5735     sh = h / 3;
5736     if ((subWin = subwin(win, sh, sw, by + h - sh - 2, bx + w - sw - 2)) == 0)
5737         return;
5738
5739 #ifdef A_COLOR
5740     if (use_colors) {
5741         init_pair(2, COLOR_CYAN, COLOR_BLUE);
5742         wbkgd(subWin, (chtype) (COLOR_PAIR(2) | ' '));
5743     }
5744 #endif
5745     (void) wattrset(subWin, A_BOLD);
5746     box(subWin, ACS_VLINE, ACS_HLINE);
5747     MvWAddStr(subWin, 2, 1, "This is a subwindow");
5748     wrefresh(win);
5749
5750     /*
5751      * This used to set 'nocbreak()'.  However, Alexander Lukyanov says that
5752      * it only happened to "work" on SVr4 because that implementation does not
5753      * emulate nocbreak+noecho mode, whereas ncurses does.  To get the desired
5754      * test behavior, we're using 'cbreak()', which will allow a single
5755      * character to return without needing a newline. - T.Dickey 1997/10/11.
5756      */
5757     cbreak();
5758     MvWAddStr(win, 0, 1, "This is a test of the flushinp() call.");
5759
5760     MvWAddStr(win, 2, 1, "Type random keys for 5 seconds.");
5761     MvWAddStr(win, 3, 1,
5762               "These should be discarded (not echoed) after the subwindow goes away.");
5763     wrefresh(win);
5764
5765     for (i = 0; i < 5; i++) {
5766         MvWPrintw(subWin, 1, 1, "Time = %d", i);
5767         wrefresh(subWin);
5768         napms(1000);
5769         flushinp();
5770     }
5771
5772     delwin(subWin);
5773     werase(win);
5774     flash();
5775     wrefresh(win);
5776     napms(1000);
5777
5778     MvWAddStr(win, 2, 1,
5779               "If you were still typing when the window timer expired,");
5780     MvWAddStr(win, 3, 1,
5781               "or else you typed nothing at all while it was running,");
5782     MvWAddStr(win, 4, 1,
5783               "test was invalid.  You'll see garbage or nothing at all. ");
5784     MvWAddStr(win, 6, 1, "Press a key");
5785     wmove(win, 9, 10);
5786     wrefresh(win);
5787     echo();
5788     wGetchar(win);
5789     flushinp();
5790     MvWAddStr(win, 12, 0,
5791               "If you see any key other than what you typed, flushinp() is broken.");
5792     Continue(win);
5793
5794     wmove(win, 9, 10);
5795     wdelch(win);
5796     wrefresh(win);
5797     wmove(win, 12, 0);
5798     clrtoeol();
5799     waddstr(win,
5800             "What you typed should now have been deleted; if not, wdelch() failed.");
5801     Continue(win);
5802
5803     cbreak();
5804 }
5805
5806 /****************************************************************************
5807  *
5808  * Menu test
5809  *
5810  ****************************************************************************/
5811
5812 #if USE_LIBMENU
5813
5814 #define MENU_Y  8
5815 #define MENU_X  8
5816
5817 static int
5818 menu_virtualize(int c)
5819 {
5820     if (c == '\n' || c == KEY_EXIT)
5821         return (MAX_COMMAND + 1);
5822     else if (c == 'u')
5823         return (REQ_SCR_ULINE);
5824     else if (c == 'd')
5825         return (REQ_SCR_DLINE);
5826     else if (c == 'b' || c == KEY_NPAGE)
5827         return (REQ_SCR_UPAGE);
5828     else if (c == 'f' || c == KEY_PPAGE)
5829         return (REQ_SCR_DPAGE);
5830     else if (c == 'n' || c == KEY_DOWN)
5831         return (REQ_NEXT_ITEM);
5832     else if (c == 'p' || c == KEY_UP)
5833         return (REQ_PREV_ITEM);
5834     else if (c == ' ')
5835         return (REQ_TOGGLE_ITEM);
5836     else {
5837         if (c != KEY_MOUSE)
5838             beep();
5839         return (c);
5840     }
5841 }
5842
5843 static CONST_MENUS char *animals[] =
5844 {
5845     "Lions",
5846     "Tigers",
5847     "Bears",
5848     "(Oh my!)",
5849     "Newts",
5850     "Platypi",
5851     "Lemurs",
5852     "(Oh really?!)",
5853     "Leopards",
5854     "Panthers",
5855     "Pumas",
5856     "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
5857     "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs, Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
5858     (char *) 0
5859 };
5860
5861 static void
5862 menu_test(void)
5863 {
5864     MENU *m;
5865     ITEM *items[SIZEOF(animals)];
5866     ITEM **ip = items;
5867     CONST_MENUS char **ap;
5868     int mrows, mcols, c;
5869     WINDOW *menuwin;
5870
5871 #ifdef NCURSES_MOUSE_VERSION
5872     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
5873 #endif
5874     MvAddStr(0, 0, "This is the menu test:");
5875     MvAddStr(2, 0, "  Use up and down arrow to move the select bar.");
5876     MvAddStr(3, 0, "  'n' and 'p' act like arrows.");
5877     MvAddStr(4, 0,
5878              "  'b' and 'f' scroll up/down (page), 'u' and 'd' (line).");
5879     MvAddStr(5, 0, "  Press return to exit.");
5880     refresh();
5881
5882     for (ap = animals; *ap; ap++) {
5883         if ((*ip = new_item(*ap, "")) != 0)
5884             ++ip;
5885     }
5886     *ip = (ITEM *) 0;
5887
5888     m = new_menu(items);
5889
5890     set_menu_format(m, (SIZEOF(animals) + 1) / 2, 1);
5891     scale_menu(m, &mrows, &mcols);
5892
5893     menuwin = newwin(mrows + 2, mcols + 2, MENU_Y, MENU_X);
5894     set_menu_win(m, menuwin);
5895     keypad(menuwin, TRUE);
5896     box(menuwin, 0, 0);
5897
5898     set_menu_sub(m, derwin(menuwin, mrows, mcols, 1, 1));
5899
5900     post_menu(m);
5901
5902     while ((c = menu_driver(m, menu_virtualize(wGetchar(menuwin)))) != E_UNKNOWN_COMMAND) {
5903         if (c == E_NOT_POSTED)
5904             break;
5905         if (c == E_REQUEST_DENIED)
5906             beep();
5907         continue;
5908     }
5909
5910     MvPrintw(LINES - 2, 0,
5911              "You chose: %s\n", item_name(current_item(m)));
5912     (void) addstr("Press any key to continue...");
5913     wGetchar(stdscr);
5914
5915     unpost_menu(m);
5916     delwin(menuwin);
5917
5918     free_menu(m);
5919     for (ip = items; *ip; ip++)
5920         free_item(*ip);
5921 #ifdef NCURSES_MOUSE_VERSION
5922     mousemask(0, (mmask_t *) 0);
5923 #endif
5924 }
5925
5926 #ifdef TRACE
5927 #define T_TBL(name) { #name, name }
5928 static struct {
5929     const char *name;
5930     unsigned mask;
5931 } t_tbl[] = {
5932
5933     T_TBL(TRACE_DISABLE),
5934         T_TBL(TRACE_TIMES),
5935         T_TBL(TRACE_TPUTS),
5936         T_TBL(TRACE_UPDATE),
5937         T_TBL(TRACE_MOVE),
5938         T_TBL(TRACE_CHARPUT),
5939         T_TBL(TRACE_ORDINARY),
5940         T_TBL(TRACE_CALLS),
5941         T_TBL(TRACE_VIRTPUT),
5942         T_TBL(TRACE_IEVENT),
5943         T_TBL(TRACE_BITS),
5944         T_TBL(TRACE_ICALLS),
5945         T_TBL(TRACE_CCALLS),
5946         T_TBL(TRACE_DATABASE),
5947         T_TBL(TRACE_ATTRS),
5948         T_TBL(TRACE_MAXIMUM),
5949     {
5950         (char *) 0, 0
5951     }
5952 };
5953
5954 static char *
5955 tracetrace(unsigned tlevel)
5956 {
5957     static char *buf;
5958     static size_t need = 12;
5959     int n;
5960
5961     if (buf == 0) {
5962         for (n = 0; t_tbl[n].name != 0; n++)
5963             need += strlen(t_tbl[n].name) + 2;
5964         buf = typeMalloc(char, need);
5965         if (!buf)
5966             failed("tracetrace");
5967     }
5968     _nc_SPRINTF(buf, _nc_SLIMIT(need) "0x%02x = {", tlevel);
5969     if (tlevel == 0) {
5970         _nc_STRCAT(buf, t_tbl[0].name, need);
5971         _nc_STRCAT(buf, ", ", need);
5972     } else {
5973         for (n = 1; t_tbl[n].name != 0; n++)
5974             if ((tlevel & t_tbl[n].mask) == t_tbl[n].mask) {
5975                 _nc_STRCAT(buf, t_tbl[n].name, need);
5976                 _nc_STRCAT(buf, ", ", need);
5977             }
5978     }
5979     if (buf[strlen(buf) - 2] == ',')
5980         buf[strlen(buf) - 2] = '\0';
5981     _nc_STRCAT(buf, "}", need);
5982     return buf;
5983 }
5984
5985 /* fake a dynamically reconfigurable menu using the 0th entry to deselect
5986  * the others
5987  */
5988 static int
5989 run_trace_menu(MENU * m)
5990 {
5991     ITEM **items;
5992     ITEM *i, **p;
5993
5994     for (;;) {
5995         bool changed = FALSE;
5996         switch (menu_driver(m, menu_virtualize(wGetchar(menu_win(m))))) {
5997         case E_UNKNOWN_COMMAND:
5998             return FALSE;
5999         default:
6000             items = menu_items(m);
6001             i = current_item(m);
6002             if (i == items[0]) {
6003                 if (item_value(i)) {
6004                     for (p = items + 1; *p != 0; p++)
6005                         if (item_value(*p)) {
6006                             set_item_value(*p, FALSE);
6007                             changed = TRUE;
6008                         }
6009                 }
6010             } else {
6011                 for (p = items + 1; *p != 0; p++)
6012                     if (item_value(*p)) {
6013                         set_item_value(items[0], FALSE);
6014                         changed = TRUE;
6015                         break;
6016                     }
6017             }
6018             if (!changed)
6019                 return TRUE;
6020         }
6021     }
6022 }
6023
6024 static void
6025 trace_set(void)
6026 /* interactively set the trace level */
6027 {
6028     MENU *m;
6029     ITEM *items[SIZEOF(t_tbl)];
6030     ITEM **ip = items;
6031     int mrows, mcols;
6032     unsigned newtrace;
6033     int n;
6034     WINDOW *menuwin;
6035
6036     MvAddStr(0, 0, "Interactively set trace level:");
6037     MvAddStr(2, 0, "  Press space bar to toggle a selection.");
6038     MvAddStr(3, 0, "  Use up and down arrow to move the select bar.");
6039     MvAddStr(4, 0, "  Press return to set the trace level.");
6040     MvPrintw(6, 0, "(Current trace level is %s)", tracetrace(_nc_tracing));
6041
6042     refresh();
6043
6044     for (n = 0; t_tbl[n].name != 0; n++) {
6045         if ((*ip = new_item(t_tbl[n].name, "")) != 0) {
6046             ++ip;
6047         }
6048     }
6049     *ip = (ITEM *) 0;
6050
6051     m = new_menu(items);
6052
6053     set_menu_format(m, 0, 2);
6054     scale_menu(m, &mrows, &mcols);
6055
6056     menu_opts_off(m, O_ONEVALUE);
6057     menuwin = newwin(mrows + 2, mcols + 2, MENU_Y, MENU_X);
6058     set_menu_win(m, menuwin);
6059     keypad(menuwin, TRUE);
6060     box(menuwin, 0, 0);
6061
6062     set_menu_sub(m, derwin(menuwin, mrows, mcols, 1, 1));
6063
6064     post_menu(m);
6065
6066     for (ip = menu_items(m); *ip; ip++) {
6067         unsigned mask = t_tbl[item_index(*ip)].mask;
6068         if (mask == 0)
6069             set_item_value(*ip, _nc_tracing == 0);
6070         else if ((mask & _nc_tracing) == mask)
6071             set_item_value(*ip, TRUE);
6072     }
6073
6074     while (run_trace_menu(m))
6075         continue;
6076
6077     newtrace = 0;
6078     for (ip = menu_items(m); *ip; ip++)
6079         if (item_value(*ip))
6080             newtrace |= t_tbl[item_index(*ip)].mask;
6081     trace(newtrace);
6082     Trace(("trace level interactively set to %s", tracetrace(_nc_tracing)));
6083
6084     MvPrintw(LINES - 2, 0,
6085              "Trace level is %s\n", tracetrace(_nc_tracing));
6086     (void) addstr("Press any key to continue...");
6087     wGetchar(stdscr);
6088
6089     unpost_menu(m);
6090     delwin(menuwin);
6091
6092     free_menu(m);
6093     for (ip = items; *ip; ip++)
6094         free_item(*ip);
6095 }
6096 #endif /* TRACE */
6097 #endif /* USE_LIBMENU */
6098
6099 /****************************************************************************
6100  *
6101  * Forms test
6102  *
6103  ****************************************************************************/
6104 #if USE_LIBFORM
6105 static FIELD *
6106 make_label(int frow, int fcol, NCURSES_CONST char *label)
6107 {
6108     FIELD *f = new_field(1, (int) strlen(label), frow, fcol, 0, 0);
6109
6110     if (f) {
6111         set_field_buffer(f, 0, label);
6112         set_field_opts(f, (int) ((unsigned) field_opts(f) & ~O_ACTIVE));
6113     }
6114     return (f);
6115 }
6116
6117 static FIELD *
6118 make_field(int frow, int fcol, int rows, int cols, bool secure)
6119 {
6120     FIELD *f = new_field(rows, cols, frow, fcol, 0, secure ? 1 : 0);
6121
6122     if (f) {
6123         set_field_back(f, A_UNDERLINE);
6124         set_field_userptr(f, (void *) 0);
6125     }
6126     return (f);
6127 }
6128
6129 static void
6130 display_form(FORM *f)
6131 {
6132     WINDOW *w;
6133     int rows, cols;
6134
6135     scale_form(f, &rows, &cols);
6136
6137     if ((w = newwin(rows + 2, cols + 4, 0, 0)) != (WINDOW *) 0) {
6138         set_form_win(f, w);
6139         set_form_sub(f, derwin(w, rows, cols, 1, 2));
6140         box(w, 0, 0);
6141         keypad(w, TRUE);
6142         if (post_form(f) != E_OK)
6143             wrefresh(w);
6144     }
6145 }
6146
6147 static void
6148 erase_form(FORM *f)
6149 {
6150     WINDOW *w = form_win(f);
6151     WINDOW *s = form_sub(f);
6152
6153     unpost_form(f);
6154     werase(w);
6155     wrefresh(w);
6156     delwin(s);
6157     delwin(w);
6158 }
6159
6160 static int
6161 edit_secure(FIELD *me, int c)
6162 {
6163     int rows, cols, frow, fcol, nrow, nbuf;
6164
6165     if (field_info(me, &rows, &cols, &frow, &fcol, &nrow, &nbuf) == E_OK
6166         && nbuf > 0) {
6167         char *source = field_buffer(me, 1);
6168         size_t have = (source ? strlen(source) : 0) + 1;
6169         size_t need = 80 + have;
6170         char *temp = malloc(need);
6171         size_t len;
6172
6173         if (temp != 0) {
6174             _nc_STRNCPY(temp, source ? source : "", have + 1);
6175             len = (size_t) (char *) field_userptr(me);
6176             if (c <= KEY_MAX) {
6177                 if (isgraph(c) && (len + 1) < sizeof(temp)) {
6178                     temp[len++] = (char) c;
6179                     temp[len] = 0;
6180                     set_field_buffer(me, 1, temp);
6181                     c = '*';
6182                 } else {
6183                     c = 0;
6184                 }
6185             } else {
6186                 switch (c) {
6187                 case REQ_BEG_FIELD:
6188                 case REQ_CLR_EOF:
6189                 case REQ_CLR_EOL:
6190                 case REQ_DEL_LINE:
6191                 case REQ_DEL_WORD:
6192                 case REQ_DOWN_CHAR:
6193                 case REQ_END_FIELD:
6194                 case REQ_INS_CHAR:
6195                 case REQ_INS_LINE:
6196                 case REQ_LEFT_CHAR:
6197                 case REQ_NEW_LINE:
6198                 case REQ_NEXT_WORD:
6199                 case REQ_PREV_WORD:
6200                 case REQ_RIGHT_CHAR:
6201                 case REQ_UP_CHAR:
6202                     c = 0;      /* we don't want to do inline editing */
6203                     break;
6204                 case REQ_CLR_FIELD:
6205                     if (len) {
6206                         temp[0] = 0;
6207                         set_field_buffer(me, 1, temp);
6208                     }
6209                     break;
6210                 case REQ_DEL_CHAR:
6211                 case REQ_DEL_PREV:
6212                     if (len) {
6213                         temp[--len] = 0;
6214                         set_field_buffer(me, 1, temp);
6215                     }
6216                     break;
6217                 }
6218             }
6219             set_field_userptr(me, (void *) len);
6220             free(temp);
6221         }
6222     }
6223     return c;
6224 }
6225
6226 static int
6227 form_virtualize(FORM *f, WINDOW *w)
6228 {
6229     /* *INDENT-OFF* */
6230     static const struct {
6231         int code;
6232         int result;
6233     } lookup[] = {
6234         { CTRL('A'),    REQ_NEXT_CHOICE },
6235         { CTRL('B'),    REQ_PREV_WORD },
6236         { CTRL('C'),    REQ_CLR_EOL },
6237         { CTRL('D'),    REQ_DOWN_FIELD },
6238         { CTRL('E'),    REQ_END_FIELD },
6239         { CTRL('F'),    REQ_NEXT_PAGE },
6240         { CTRL('G'),    REQ_DEL_WORD },
6241         { CTRL('H'),    REQ_DEL_PREV },
6242         { CTRL('I'),    REQ_INS_CHAR },
6243         { CTRL('K'),    REQ_CLR_EOF },
6244         { CTRL('L'),    REQ_LEFT_FIELD },
6245         { CTRL('M'),    REQ_NEW_LINE },
6246         { CTRL('N'),    REQ_NEXT_FIELD },
6247         { CTRL('O'),    REQ_INS_LINE },
6248         { CTRL('P'),    REQ_PREV_FIELD },
6249         { CTRL('R'),    REQ_RIGHT_FIELD },
6250         { CTRL('S'),    REQ_BEG_FIELD },
6251         { CTRL('U'),    REQ_UP_FIELD },
6252         { CTRL('V'),    REQ_DEL_CHAR },
6253         { CTRL('W'),    REQ_NEXT_WORD },
6254         { CTRL('X'),    REQ_CLR_FIELD },
6255         { CTRL('Y'),    REQ_DEL_LINE },
6256         { CTRL('Z'),    REQ_PREV_CHOICE },
6257         { ESCAPE,       MAX_FORM_COMMAND + 1 },
6258         { KEY_BACKSPACE, REQ_DEL_PREV },
6259         { KEY_DOWN,     REQ_DOWN_CHAR },
6260         { KEY_END,      REQ_LAST_FIELD },
6261         { KEY_HOME,     REQ_FIRST_FIELD },
6262         { KEY_LEFT,     REQ_LEFT_CHAR },
6263         { KEY_LL,       REQ_LAST_FIELD },
6264         { KEY_NEXT,     REQ_NEXT_FIELD },
6265         { KEY_NPAGE,    REQ_NEXT_PAGE },
6266         { KEY_PPAGE,    REQ_PREV_PAGE },
6267         { KEY_PREVIOUS, REQ_PREV_FIELD },
6268         { KEY_RIGHT,    REQ_RIGHT_CHAR },
6269         { KEY_UP,       REQ_UP_CHAR },
6270         { QUIT,         MAX_FORM_COMMAND + 1 }
6271     };
6272     /* *INDENT-ON* */
6273
6274     static int mode = REQ_INS_MODE;
6275     int c = wGetchar(w);
6276     unsigned n;
6277     FIELD *me = current_field(f);
6278     bool current = TRUE;
6279
6280     if (c == CTRL(']')) {
6281         if (mode == REQ_INS_MODE) {
6282             mode = REQ_OVL_MODE;
6283         } else {
6284             mode = REQ_INS_MODE;
6285         }
6286         c = mode;
6287     } else {
6288         for (n = 0; n < SIZEOF(lookup); n++) {
6289             if (lookup[n].code == c) {
6290                 c = lookup[n].result;
6291                 break;
6292             }
6293         }
6294     }
6295     MvPrintw(0, COLS - 6, "(%s)", mode == REQ_INS_MODE ? "INS" : "OVL");
6296
6297     /*
6298      * Force the field that the user is typing into to be in reverse video,
6299      * while the other fields are shown underlined.
6300      */
6301     switch (c) {
6302     case REQ_BEG_FIELD:
6303     case REQ_CLR_EOF:
6304     case REQ_CLR_EOL:
6305     case REQ_CLR_FIELD:
6306     case REQ_DEL_CHAR:
6307     case REQ_DEL_LINE:
6308     case REQ_DEL_PREV:
6309     case REQ_DEL_WORD:
6310     case REQ_END_FIELD:
6311     case REQ_INS_CHAR:
6312     case REQ_INS_LINE:
6313     case REQ_LEFT_CHAR:
6314     case REQ_LEFT_FIELD:
6315     case REQ_NEXT_WORD:
6316     case REQ_RIGHT_CHAR:
6317         current = TRUE;
6318         break;
6319     default:
6320         current = (c < KEY_MAX);
6321         break;
6322     }
6323     if (current) {
6324         c = edit_secure(me, c);
6325         set_field_back(me, A_REVERSE);
6326     } else {
6327         c = edit_secure(me, c);
6328         set_field_back(me, A_UNDERLINE);
6329     }
6330     return c;
6331 }
6332
6333 static int
6334 my_form_driver(FORM *form, int c)
6335 {
6336     if (c == (MAX_FORM_COMMAND + 1)
6337         && form_driver(form, REQ_VALIDATION) == E_OK)
6338         return (TRUE);
6339     else {
6340         beep();
6341         return (FALSE);
6342     }
6343 }
6344
6345 #ifdef NCURSES_VERSION
6346 #define FIELDCHECK_CB(func) bool func(FIELD * fld, const void * data GCC_UNUSED)
6347 #define CHAR_CHECK_CB(func) bool func(int ch, const void *data GCC_UNUSED)
6348 #else
6349 #define FIELDCHECK_CB(func) int func(FIELD * fld, char * data GCC_UNUSED)
6350 #define CHAR_CHECK_CB(func) int func(int ch, char *data GCC_UNUSED)
6351 #endif
6352
6353 /*
6354  * Allow a middle initial, optionally with a '.' to end it.
6355  */
6356 static
6357 FIELDCHECK_CB(mi_field_check)
6358 {
6359     char *s = field_buffer(fld, 0);
6360     int state = 0;
6361     int n;
6362
6363     for (n = 0; s[n] != '\0'; ++n) {
6364         switch (state) {
6365         case 0:
6366             if (s[n] == '.') {
6367                 if (n != 1)
6368                     return FALSE;
6369                 state = 2;
6370             } else if (isspace(UChar(s[n]))) {
6371                 state = 2;
6372             }
6373             break;
6374         case 2:
6375             if (!isspace(UChar(s[n])))
6376                 return FALSE;
6377             break;
6378         }
6379     }
6380
6381     /* force the form to display a leading capital */
6382     if (islower(UChar(s[0]))) {
6383         s[0] = (char) toupper(UChar(s[0]));
6384         set_field_buffer(fld, 0, s);
6385     }
6386     return TRUE;
6387 }
6388
6389 static
6390 CHAR_CHECK_CB(mi_char_check)
6391 {
6392     return ((isalpha(ch) || ch == '.') ? TRUE : FALSE);
6393 }
6394
6395 /*
6396  * Passwords should be at least 6 characters.
6397  */
6398 static
6399 FIELDCHECK_CB(pw_field_check)
6400 {
6401     char *s = field_buffer(fld, 0);
6402     int n;
6403
6404     for (n = 0; s[n] != '\0'; ++n) {
6405         if (isspace(UChar(s[n]))) {
6406             if (n < 6)
6407                 return FALSE;
6408         }
6409     }
6410     return TRUE;
6411 }
6412
6413 static
6414 CHAR_CHECK_CB(pw_char_check)
6415 {
6416     return (isgraph(ch) ? TRUE : FALSE);
6417 }
6418
6419 static void
6420 demo_forms(void)
6421 {
6422     WINDOW *w;
6423     FORM *form;
6424     FIELD *f[12], *secure;
6425     FIELDTYPE *fty_middle = new_fieldtype(mi_field_check, mi_char_check);
6426     FIELDTYPE *fty_passwd = new_fieldtype(pw_field_check, pw_char_check);
6427     int finished = 0, c;
6428     unsigned n = 0;
6429
6430 #ifdef NCURSES_MOUSE_VERSION
6431     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
6432 #endif
6433
6434     move(18, 0);
6435     addstr("Defined edit/traversal keys:   ^Q/ESC- exit form\n");
6436     addstr("^N   -- go to next field       ^P  -- go to previous field\n");
6437     addstr("Home -- go to first field      End -- go to last field\n");
6438     addstr("^L   -- go to field to left    ^R  -- go to field to right\n");
6439     addstr("^U   -- move upward to field   ^D  -- move downward to field\n");
6440     addstr("^W   -- go to next word        ^B  -- go to previous word\n");
6441     addstr("^S   -- go to start of field   ^E  -- go to end of field\n");
6442     addstr("^H   -- delete previous char   ^Y  -- delete line\n");
6443     addstr("^G   -- delete current word    ^C  -- clear to end of line\n");
6444     addstr("^K   -- clear to end of field  ^X  -- clear field\n");
6445     addstr("Arrow keys move within a field as you would expect. ^] toggles overlay mode.");
6446
6447     MvAddStr(4, 57, "Forms Entry Test");
6448
6449     refresh();
6450
6451     /* describe the form */
6452     memset(f, 0, sizeof(f));
6453     f[n++] = make_label(0, 15, "Sample Form");
6454
6455     f[n++] = make_label(2, 0, "Last Name");
6456     f[n++] = make_field(3, 0, 1, 18, FALSE);
6457     set_field_type(f[n - 1], TYPE_ALPHA, 1);
6458
6459     f[n++] = make_label(2, 20, "First Name");
6460     f[n++] = make_field(3, 20, 1, 12, FALSE);
6461     set_field_type(f[n - 1], TYPE_ALPHA, 1);
6462
6463     f[n++] = make_label(2, 34, "Middle Name");
6464     f[n++] = make_field(3, 34, 1, 12, FALSE);
6465     set_field_type(f[n - 1], fty_middle);
6466
6467     f[n++] = make_label(5, 0, "Comments");
6468     f[n++] = make_field(6, 0, 4, 46, FALSE);
6469
6470     f[n++] = make_label(5, 20, "Password:");
6471     secure =
6472         f[n++] = make_field(5, 30, 1, 9, TRUE);
6473     set_field_type(f[n - 1], fty_passwd);
6474     f[n] = (FIELD *) 0;
6475
6476     if ((form = new_form(f)) != 0) {
6477
6478         display_form(form);
6479
6480         w = form_win(form);
6481         raw();
6482         nonl();                 /* lets us read ^M's */
6483         while (!finished) {
6484             switch (form_driver(form, c = form_virtualize(form, w))) {
6485             case E_OK:
6486                 MvAddStr(5, 57, field_buffer(secure, 1));
6487                 clrtoeol();
6488                 refresh();
6489                 break;
6490             case E_UNKNOWN_COMMAND:
6491                 finished = my_form_driver(form, c);
6492                 break;
6493             default:
6494                 beep();
6495                 break;
6496             }
6497         }
6498
6499         erase_form(form);
6500
6501         free_form(form);
6502     }
6503     for (c = 0; f[c] != 0; c++)
6504         free_field(f[c]);
6505     free_fieldtype(fty_middle);
6506     free_fieldtype(fty_passwd);
6507     noraw();
6508     nl();
6509
6510 #ifdef NCURSES_MOUSE_VERSION
6511     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
6512 #endif
6513 }
6514 #endif /* USE_LIBFORM */
6515
6516 /****************************************************************************
6517  *
6518  * Overlap test
6519  *
6520  ****************************************************************************/
6521
6522 static void
6523 fillwin(WINDOW *win, char ch)
6524 {
6525     int y, x;
6526     int y1, x1;
6527
6528     getmaxyx(win, y1, x1);
6529     for (y = 0; y < y1; y++) {
6530         wmove(win, y, 0);
6531         for (x = 0; x < x1; x++)
6532             waddch(win, UChar(ch));
6533     }
6534 }
6535
6536 static void
6537 crosswin(WINDOW *win, char ch)
6538 {
6539     int y, x;
6540     int y1, x1;
6541
6542     getmaxyx(win, y1, x1);
6543     for (y = 0; y < y1; y++) {
6544         for (x = 0; x < x1; x++)
6545             if (((x > (x1 - 1) / 3) && (x <= (2 * (x1 - 1)) / 3))
6546                 || (((y > (y1 - 1) / 3) && (y <= (2 * (y1 - 1)) / 3)))) {
6547                 wmove(win, y, x);
6548                 waddch(win, UChar(ch));
6549             }
6550     }
6551 }
6552
6553 #define OVERLAP_FLAVORS 5
6554
6555 static void
6556 overlap_helpitem(int state, int item, char *message)
6557 {
6558     int row = (item / 2);
6559     int col = ((item % 2) ? COLS / 2 : 0);
6560
6561     move(LINES - 6 + row, col);
6562     printw("%c%c = %s", state == row ? '>' : ' ', 'a' + item, message);
6563     clrtoeol();
6564 }
6565
6566 static void
6567 overlap_test_1_attr(WINDOW *win, int flavor, int col)
6568 {
6569     NCURSES_PAIRS_T cpair = (NCURSES_PAIRS_T) (1 + (flavor * 2) + col);
6570
6571     switch (flavor) {
6572     case 0:
6573         (void) wattrset(win, A_NORMAL);
6574         break;
6575     case 1:
6576         (void) wattrset(win, A_BOLD);
6577         break;
6578     case 2:
6579         init_pair(cpair, COLOR_BLUE, COLOR_WHITE);
6580         (void) wattrset(win, AttrArg(COLOR_PAIR(cpair), A_NORMAL));
6581         break;
6582     case 3:
6583         init_pair(cpair, COLOR_WHITE, COLOR_BLUE);
6584         (void) wattrset(win, AttrArg(COLOR_PAIR(cpair), A_BOLD));
6585         break;
6586     }
6587 }
6588
6589 static void
6590 overlap_test_2_attr(WINDOW *win, int flavor, int col)
6591 {
6592     NCURSES_PAIRS_T cpair = (NCURSES_PAIRS_T) (9 + (flavor * 2) + col);
6593
6594     switch (flavor) {
6595     case 0:
6596         /* no effect */
6597         break;
6598     case 1:
6599         /* no effect */
6600         break;
6601     case 2:
6602         init_pair(cpair, COLOR_RED, COLOR_GREEN);
6603         wbkgdset(win, colored_chtype(' ', A_BLINK, cpair));
6604         break;
6605     case 3:
6606         wbkgdset(win, ' ' | A_NORMAL);
6607         break;
6608     }
6609 }
6610
6611 static int
6612 overlap_help(int state, int flavors[OVERLAP_FLAVORS])
6613 {
6614     int row;
6615     int col;
6616     int item;
6617     const char *ths, *tht;
6618     char msg[80];
6619
6620     if (state < 0)
6621         state += OVERLAP_FLAVORS;
6622     state = state % OVERLAP_FLAVORS;
6623     assert(state >= 0 && state < OVERLAP_FLAVORS);
6624
6625     for (item = 0; item < (2 * OVERLAP_FLAVORS); ++item) {
6626         row = item / 2;
6627         col = item % 2;
6628         ths = col ? "B" : "A";
6629         tht = col ? "A" : "B";
6630
6631         switch (row) {
6632         case 0:
6633             flavors[row] = 0;
6634             _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
6635                         "refresh %s, then %s, then doupdate.", ths, tht);
6636             break;
6637         case 1:
6638             if (use_colors) {
6639                 flavors[row] %= 4;
6640             } else {
6641                 flavors[row] %= 2;
6642             }
6643             overlap_test_1_attr(stdscr, flavors[row], col);
6644             _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
6645                         "fill window %s with letter %s.", ths, ths);
6646             break;
6647         case 2:
6648             if (use_colors) {
6649                 flavors[row] %= 4;
6650             } else {
6651                 flavors[row] %= 2;
6652             }
6653             switch (flavors[row]) {
6654             case 0:
6655                 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
6656                             "cross pattern in window %s.", ths);
6657                 break;
6658             case 1:
6659                 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
6660                             "draw box in window %s.", ths);
6661                 break;
6662             case 2:
6663                 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
6664                             "set background of window %s.", ths);
6665                 break;
6666             case 3:
6667                 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
6668                             "reset background of window %s.", ths);
6669                 break;
6670             }
6671             break;
6672         case 3:
6673             flavors[row] = 0;
6674             _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
6675                         "clear window %s.", ths);
6676             break;
6677         case 4:
6678             flavors[row] %= 4;
6679             switch (flavors[row]) {
6680             case 0:
6681                 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
6682                             "overwrite %s onto %s.", ths, tht);
6683                 break;
6684             case 1:
6685                 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
6686                             "copywin(FALSE) %s onto %s.", ths, tht);
6687                 break;
6688             case 2:
6689                 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
6690                             "copywin(TRUE) %s onto %s.", ths, tht);
6691                 break;
6692             case 3:
6693                 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
6694                             "overlay %s onto %s.", ths, tht);
6695                 break;
6696             }
6697             break;
6698         }
6699         overlap_helpitem(state, item, msg);
6700         (void) wattrset(stdscr, A_NORMAL);
6701         wbkgdset(stdscr, ' ' | A_NORMAL);
6702     }
6703     move(LINES - 1, 0);
6704     printw("^Q/ESC = terminate test.  Up/down/space select test variations (%d %d).",
6705            state, flavors[state]);
6706
6707     return state;
6708 }
6709
6710 static void
6711 overlap_test_0(WINDOW *a, WINDOW *b)
6712 {
6713     touchwin(a);
6714     touchwin(b);
6715     wnoutrefresh(a);
6716     wnoutrefresh(b);
6717     doupdate();
6718 }
6719
6720 static void
6721 overlap_test_1(int flavor, int col, WINDOW *a, char fill)
6722 {
6723     overlap_test_1_attr(a, flavor, col);
6724     fillwin(a, fill);
6725     (void) wattrset(a, A_NORMAL);
6726 }
6727
6728 static void
6729 overlap_test_2(int flavor, int col, WINDOW *a, char fill)
6730 {
6731     overlap_test_2_attr(a, flavor, col);
6732     switch (flavor) {
6733     case 0:
6734         crosswin(a, fill);
6735         break;
6736     case 1:
6737         box(a, 0, 0);
6738         break;
6739     case 2:
6740         /* done in overlap_test_2_attr */
6741         break;
6742     case 3:
6743         /* done in overlap_test_2_attr */
6744         break;
6745     }
6746 }
6747
6748 static void
6749 overlap_test_3(WINDOW *a)
6750 {
6751     wclear(a);
6752     wmove(a, 0, 0);
6753 }
6754
6755 static void
6756 overlap_test_4(int flavor, WINDOW *a, WINDOW *b)
6757 {
6758     switch (flavor) {
6759     case 0:
6760         overwrite(a, b);
6761         break;
6762     case 1:
6763         copywin(a, b, 0, 0, 0, 0, getmaxy(b), getmaxx(b), FALSE);
6764         break;
6765     case 2:
6766         copywin(a, b, 0, 0, 0, 0, getmaxy(b), getmaxx(b), TRUE);
6767         break;
6768     case 3:
6769         overlay(a, b);
6770         break;
6771     }
6772 }
6773
6774 /* test effects of overlapping windows */
6775 static void
6776 overlap_test(void)
6777 {
6778     int ch;
6779     int state, flavor[OVERLAP_FLAVORS];
6780
6781     WINDOW *win1 = newwin(9, 20, 3, 3);
6782     WINDOW *win2 = newwin(9, 20, 9, 16);
6783
6784     curs_set(0);
6785     raw();
6786     refresh();
6787     move(0, 0);
6788     printw("This test shows the behavior of wnoutrefresh() with respect to\n");
6789     printw("the shared region of two overlapping windows A and B.  The cross\n");
6790     printw("pattern in each window does not overlap the other.\n");
6791
6792     memset(flavor, 0, sizeof(flavor));
6793     state = overlap_help(0, flavor);
6794
6795     while (!isQuit(ch = Getchar(), TRUE))
6796         switch (ch) {
6797         case 'a':               /* refresh window A first, then B */
6798             overlap_test_0(win1, win2);
6799             break;
6800
6801         case 'b':               /* refresh window B first, then A */
6802             overlap_test_0(win2, win1);
6803             break;
6804
6805         case 'c':               /* fill window A so it's visible */
6806             overlap_test_1(flavor[1], 0, win1, 'A');
6807             break;
6808
6809         case 'd':               /* fill window B so it's visible */
6810             overlap_test_1(flavor[1], 1, win2, 'B');
6811             break;
6812
6813         case 'e':               /* cross test pattern in window A */
6814             overlap_test_2(flavor[2], 0, win1, 'A');
6815             break;
6816
6817         case 'f':               /* cross test pattern in window A */
6818             overlap_test_2(flavor[2], 1, win2, 'B');
6819             break;
6820
6821         case 'g':               /* clear window A */
6822             overlap_test_3(win1);
6823             break;
6824
6825         case 'h':               /* clear window B */
6826             overlap_test_3(win2);
6827             break;
6828
6829         case 'i':               /* overwrite A onto B */
6830             overlap_test_4(flavor[4], win1, win2);
6831             break;
6832
6833         case 'j':               /* overwrite B onto A */
6834             overlap_test_4(flavor[4], win2, win1);
6835             break;
6836
6837         case CTRL('n'):
6838         case KEY_DOWN:
6839             state = overlap_help(state + 1, flavor);
6840             break;
6841
6842         case CTRL('p'):
6843         case KEY_UP:
6844             state = overlap_help(state - 1, flavor);
6845             break;
6846
6847         case ' ':
6848             flavor[state] += 1;
6849             state = overlap_help(state, flavor);
6850             break;
6851
6852         case HELP_KEY_1:
6853             state = overlap_help(state, flavor);
6854             break;
6855
6856         default:
6857             beep();
6858             break;
6859         }
6860
6861     delwin(win2);
6862     delwin(win1);
6863     erase();
6864     curs_set(1);
6865     endwin();
6866 }
6867
6868 static void
6869 show_setting_name(const char *name)
6870 {
6871     printw("%-25s ", name);
6872 }
6873
6874 static void
6875 show_string_setting(const char *name, const char *value)
6876 {
6877     show_setting_name(name);
6878     if (value) {
6879         printw("\"%s\"", value);
6880     } else {
6881         attron(A_REVERSE);
6882         addstr("<NULL>");
6883         attroff(A_REVERSE);
6884     }
6885     AddCh('\n');
6886 }
6887
6888 static void
6889 show_number_setting(const char *name, int value)
6890 {
6891     show_setting_name(name);
6892     if (value >= 0) {
6893         printw("%d", value);
6894     } else {
6895         attron(A_REVERSE);
6896         printw("%d", value);
6897         attroff(A_REVERSE);
6898     }
6899     AddCh('\n');
6900 }
6901
6902 static void
6903 show_boolean_setting(const char *name, int value)
6904 {
6905     show_setting_name(name);
6906     if (value >= 0) {
6907         printw("%s", value ? "TRUE" : "FALSE");
6908     } else {
6909         attron(A_REVERSE);
6910         printw("%d", value);
6911         attroff(A_REVERSE);
6912     }
6913     AddCh('\n');
6914 }
6915
6916 static void
6917 show_settings(void)
6918 {
6919 #if USE_WIDEC_SUPPORT
6920     wchar_t ch;
6921 #endif
6922
6923     move(0, 0);
6924     show_string_setting("termname", termname());
6925     show_string_setting("longname", longname());
6926     show_number_setting("baudrate", baudrate());
6927     if (erasechar() > 0) {
6928         show_string_setting("unctrl(erasechar)", unctrl((chtype) erasechar()));
6929         show_string_setting("keyname(erasechar)", keyname(erasechar()));
6930     }
6931     if (killchar() > 0) {
6932         show_string_setting("unctrl(killchar)", unctrl((chtype) killchar()));
6933         show_string_setting("keyname(killchar)", keyname(killchar()));
6934     }
6935 #if USE_WIDEC_SUPPORT
6936     if (erasewchar(&ch) == OK) {
6937         show_string_setting("key_name(erasewchar)", key_name(ch));
6938     }
6939     if (killwchar(&ch) == OK) {
6940         show_string_setting("key_name(killwchar)", key_name(ch));
6941     }
6942 #endif
6943     show_boolean_setting("has_ic", has_ic());
6944     show_boolean_setting("has_il", has_il());
6945     Pause();
6946     erase();
6947     endwin();
6948 }
6949
6950 /****************************************************************************
6951  *
6952  * Main sequence
6953  *
6954  ****************************************************************************/
6955
6956 static bool
6957 do_single_test(const char c)
6958 /* perform a single specified test */
6959 {
6960     switch (c) {
6961     case 'a':
6962         getch_test();
6963         break;
6964
6965 #if USE_WIDEC_SUPPORT
6966     case 'A':
6967         get_wch_test();
6968         break;
6969 #endif
6970
6971     case 'b':
6972         attr_test();
6973         break;
6974
6975 #if USE_WIDEC_SUPPORT
6976     case 'B':
6977         wide_attr_test();
6978         break;
6979 #endif
6980
6981     case 'c':
6982         if (!use_colors)
6983             Cannot("does not support color.");
6984         else
6985             color_test();
6986         break;
6987
6988 #if USE_WIDEC_SUPPORT
6989     case 'C':
6990         if (!use_colors)
6991             Cannot("does not support color.");
6992         else
6993             wide_color_test();
6994         break;
6995 #endif
6996
6997     case 'd':
6998         if (!use_colors)
6999             Cannot("does not support color.");
7000         else if (!can_change_color())
7001             Cannot("has hardwired color values.");
7002         else
7003             color_edit();
7004         break;
7005
7006 #if USE_SOFTKEYS
7007     case 'e':
7008         slk_test();
7009         break;
7010
7011 #if USE_WIDEC_SUPPORT
7012     case 'E':
7013         wide_slk_test();
7014         break;
7015 #endif
7016 #endif
7017
7018     case 'f':
7019         acs_display();
7020         break;
7021
7022 #if USE_WIDEC_SUPPORT
7023     case 'F':
7024         wide_acs_display();
7025         break;
7026 #endif
7027
7028 #if USE_LIBPANEL
7029     case 'o':
7030         demo_panels(init_panel, fill_panel);
7031         break;
7032 #endif
7033
7034 #if USE_WIDEC_SUPPORT && USE_LIBPANEL
7035     case 'O':
7036         demo_panels(init_wide_panel, fill_wide_panel);
7037         break;
7038 #endif
7039
7040     case 'g':
7041         acs_and_scroll();
7042         break;
7043
7044     case 'i':
7045         flushinp_test(stdscr);
7046         break;
7047
7048     case 'k':
7049         test_sgr_attributes();
7050         break;
7051
7052 #if USE_LIBMENU
7053     case 'm':
7054         menu_test();
7055         break;
7056 #endif
7057
7058     case 'p':
7059         demo_pad(FALSE);
7060         break;
7061
7062     case 'P':
7063         demo_pad(TRUE);
7064         break;
7065
7066 #if USE_LIBFORM
7067     case 'r':
7068         demo_forms();
7069         break;
7070 #endif
7071
7072     case 's':
7073         overlap_test();
7074         break;
7075
7076 #if USE_LIBMENU && defined(TRACE)
7077     case 't':
7078         trace_set();
7079         break;
7080 #endif
7081
7082     case 'v':
7083         show_settings();
7084         break;
7085
7086     case '?':
7087         break;
7088
7089     default:
7090         return FALSE;
7091     }
7092
7093     return TRUE;
7094 }
7095
7096 static void
7097 usage(void)
7098 {
7099     static const char *const tbl[] =
7100     {
7101         "Usage: ncurses [options]"
7102         ,""
7103         ,"Options:"
7104 #ifdef NCURSES_VERSION
7105         ,"  -a f,b   set default-colors (assumed white-on-black)"
7106         ,"  -d       use default-colors if terminal supports them"
7107 #endif
7108 #if HAVE_USE_ENV
7109         ,"  -E       call use_env(FALSE) to ignore $LINES and $COLUMNS"
7110 #endif
7111 #if USE_SOFTKEYS
7112         ,"  -e fmt   specify format for soft-keys test (e)"
7113 #endif
7114 #if HAVE_RIPOFFLINE
7115         ,"  -f       rip-off footer line (can repeat)"
7116         ,"  -h       rip-off header line (can repeat)"
7117 #endif
7118         ,"  -m       do not use colors"
7119         ,"  -p file  rgb values to use in 'd' rather than ncurses's builtin"
7120 #if USE_LIBPANEL
7121         ,"  -s msec  specify nominal time for panel-demo (default: 1, to hold)"
7122 #endif
7123 #if defined(NCURSES_VERSION_PATCH) && (NCURSES_VERSION_PATCH >= 20120714) && !defined(__MINGW32__)
7124         ,"  -T       call use_tioctl(TRUE) to allow SIGWINCH to override environment"
7125 #endif
7126 #ifdef TRACE
7127         ,"  -t mask  specify default trace-level (may toggle with ^T)"
7128 #endif
7129         ,"  -x       use xterm-compatible control for reading color palette"
7130     };
7131     size_t n;
7132     for (n = 0; n < SIZEOF(tbl); n++)
7133         fprintf(stderr, "%s\n", tbl[n]);
7134     ExitProgram(EXIT_FAILURE);
7135 }
7136
7137 static void
7138 set_terminal_modes(void)
7139 {
7140     noraw();
7141     cbreak();
7142     noecho();
7143     scrollok(stdscr, TRUE);
7144     idlok(stdscr, TRUE);
7145     keypad(stdscr, TRUE);
7146 }
7147
7148 #ifdef SIGUSR1
7149 static void
7150 announce_sig(int sig)
7151 {
7152     (void) fprintf(stderr, "Handled signal %d\r\n", sig);
7153 }
7154 #endif
7155
7156 #if HAVE_RIPOFFLINE
7157 static int
7158 rip_footer(WINDOW *win, int cols)
7159 {
7160     wbkgd(win, A_REVERSE);
7161     werase(win);
7162     wmove(win, 0, 0);
7163     wprintw(win, "footer: window %p, %d columns", (void *) win, cols);
7164     wnoutrefresh(win);
7165     return OK;
7166 }
7167
7168 static int
7169 rip_header(WINDOW *win, int cols)
7170 {
7171     wbkgd(win, A_REVERSE);
7172     werase(win);
7173     wmove(win, 0, 0);
7174     wprintw(win, "header: window %p, %d columns", (void *) win, cols);
7175     wnoutrefresh(win);
7176     return OK;
7177 }
7178 #endif /* HAVE_RIPOFFLINE */
7179
7180 static void
7181 main_menu(bool top)
7182 {
7183     char command;
7184
7185     do {
7186         (void) puts("This is the ncurses main menu");
7187         (void) puts("a = keyboard and mouse input test");
7188 #if USE_WIDEC_SUPPORT
7189         (void) puts("A = wide-character keyboard and mouse input test");
7190 #endif
7191         (void) puts("b = character attribute test");
7192 #if USE_WIDEC_SUPPORT
7193         (void) puts("B = wide-character attribute test");
7194 #endif
7195         (void) puts("c = color test pattern");
7196 #if USE_WIDEC_SUPPORT
7197         (void) puts("C = color test pattern using wide-character calls");
7198 #endif
7199         if (top)
7200             (void) puts("d = edit RGB color values");
7201 #if USE_SOFTKEYS
7202         (void) puts("e = exercise soft keys");
7203 #if USE_WIDEC_SUPPORT
7204         (void) puts("E = exercise soft keys using wide-characters");
7205 #endif
7206 #endif
7207         (void) puts("f = display ACS characters");
7208 #if USE_WIDEC_SUPPORT
7209         (void) puts("F = display Wide-ACS characters");
7210 #endif
7211         (void) puts("g = display windows and scrolling");
7212         (void) puts("i = test of flushinp()");
7213         (void) puts("k = display character attributes");
7214 #if USE_LIBMENU
7215         (void) puts("m = menu code test");
7216 #endif
7217 #if USE_LIBPANEL
7218         (void) puts("o = exercise panels library");
7219 #if USE_WIDEC_SUPPORT
7220         (void) puts("O = exercise panels with wide-characters");
7221 #endif
7222 #endif
7223         (void) puts("p = exercise pad features");
7224         (void) puts("P = exercise pad features, using color");
7225         (void) puts("q = quit");
7226 #if USE_LIBFORM
7227         (void) puts("r = exercise forms code");
7228 #endif
7229         (void) puts("s = overlapping-refresh test");
7230 #if USE_LIBMENU && defined(TRACE)
7231         (void) puts("t = set trace level");
7232 #endif
7233         (void) puts("v = show terminal name and settings");
7234         (void) puts("? = repeat this command summary");
7235
7236         (void) fputs("> ", stdout);
7237         (void) fflush(stdout);  /* necessary under SVr4 curses */
7238
7239         /*
7240          * This used to be an 'fgets()' call (until 1996/10).  However with
7241          * some runtime libraries, mixing stream I/O and 'read()' causes the
7242          * input stream to be flushed when switching between the two.
7243          */
7244         command = 0;
7245         for (;;) {
7246             char ch = '\0';
7247             if (read(fileno(stdin), &ch, (size_t) 1) <= 0) {
7248                 if (command == 0)
7249                     command = 'q';
7250                 break;
7251             } else if (command == 0 && !isspace(UChar(ch))) {
7252                 command = ch;
7253             } else if (ch == '\n' || ch == '\r') {
7254                 if ((command == 'd') && !top) {
7255                     (void) fputs("Do not nest test-d\n", stdout);
7256                     command = 0;
7257                 }
7258                 if (command != 0)
7259                     break;
7260                 (void) fputs("> ", stdout);
7261                 (void) fflush(stdout);
7262             }
7263         }
7264
7265         if (do_single_test(command)) {
7266             /*
7267              * This may be overkill; it's intended to reset everything back
7268              * to the initial terminal modes so that tests don't get in
7269              * each other's way.
7270              */
7271             flushinp();
7272             set_terminal_modes();
7273             reset_prog_mode();
7274             clear();
7275             refresh();
7276             endwin();
7277             if (command == '?') {
7278                 (void) puts("This is the ncurses capability tester.");
7279                 (void)
7280                     puts("You may select a test from the main menu by typing the");
7281                 (void)
7282                     puts("key letter of the choice (the letter to left of the =)");
7283                 (void)
7284                     puts("at the > prompt.  Type `q' to exit.");
7285             }
7286             continue;
7287         }
7288     } while
7289         (command != 'q');
7290 }
7291
7292 /*+-------------------------------------------------------------------------
7293         main(argc,argv)
7294 --------------------------------------------------------------------------*/
7295
7296 int
7297 main(int argc, char *argv[])
7298 {
7299     int c;
7300     int my_e_param = 1;
7301 #ifdef NCURSES_VERSION
7302     int default_fg = COLOR_WHITE;
7303     int default_bg = COLOR_BLACK;
7304     bool assumed_colors = FALSE;
7305     bool default_colors = FALSE;
7306 #endif
7307     char *palette_file = 0;
7308     bool monochrome = FALSE;
7309     bool xterm_colors = FALSE;
7310
7311     setlocale(LC_ALL, "");
7312
7313     while ((c = getopt(argc, argv, "a:dEe:fhmp:s:Tt:x")) != -1) {
7314         switch (c) {
7315 #ifdef NCURSES_VERSION
7316         case 'a':
7317             assumed_colors = TRUE;
7318             switch (sscanf(optarg, "%d,%d", &default_fg, &default_bg)) {
7319             case 0:
7320                 default_fg = COLOR_WHITE;
7321                 /* FALLTHRU */
7322             case 1:
7323                 default_bg = COLOR_BLACK;
7324                 break;
7325             }
7326             break;
7327         case 'd':
7328             default_colors = TRUE;
7329             break;
7330 #endif
7331 #if HAVE_USE_ENV
7332         case 'E':
7333             use_env(FALSE);
7334             break;
7335 #endif
7336         case 'e':
7337             my_e_param = atoi(optarg);
7338 #ifdef NCURSES_VERSION
7339             if (my_e_param > 3) /* allow extended layouts */
7340                 usage();
7341 #else
7342             if (my_e_param > 1)
7343                 usage();
7344 #endif
7345             break;
7346 #if HAVE_RIPOFFLINE
7347         case 'f':
7348             ripoffline(-1, rip_footer);
7349             break;
7350         case 'h':
7351             ripoffline(1, rip_header);
7352             break;
7353 #endif /* HAVE_RIPOFFLINE */
7354         case 'm':
7355             monochrome = TRUE;
7356             break;
7357         case 'p':
7358             palette_file = optarg;
7359             break;
7360 #if USE_LIBPANEL
7361         case 's':
7362             nap_msec = (int) atol(optarg);
7363             break;
7364 #endif
7365 #if defined(NCURSES_VERSION_PATCH) && (NCURSES_VERSION_PATCH >= 20120714) && !defined(__MINGW32__)
7366         case 'T':
7367             use_tioctl(TRUE);
7368             break;
7369 #endif
7370 #ifdef TRACE
7371         case 't':
7372             save_trace = (unsigned) strtol(optarg, 0, 0);
7373             break;
7374 #endif
7375         case 'x':
7376             xterm_colors = TRUE;
7377             break;
7378         default:
7379             usage();
7380         }
7381     }
7382
7383     /*
7384      * If there's no menus (unlikely for ncurses!), then we'll have to set
7385      * tracing on initially, just in case the user wants to test something that
7386      * doesn't involve wGetchar.
7387      */
7388 #ifdef TRACE
7389     /* enable debugging */
7390 #if !USE_LIBMENU
7391     trace(save_trace);
7392 #else
7393     if (!isatty(fileno(stdin)))
7394         trace(save_trace);
7395 #endif /* USE_LIBMENU */
7396 #endif /* TRACE */
7397
7398 #if USE_SOFTKEYS
7399     /* tell it we're going to play with soft keys */
7400     slk_init(my_e_param);
7401 #endif
7402
7403 #ifdef SIGUSR1
7404     /* set up null signal catcher so we can see what interrupts to getch do */
7405     signal(SIGUSR1, announce_sig);
7406 #endif
7407
7408     /* we must initialize the curses data structure only once */
7409     initscr();
7410     bkgdset(BLANK);
7411
7412     set_terminal_modes();
7413     def_prog_mode();
7414
7415     /* tests, in general, will want these modes */
7416     use_colors = (bool) (monochrome ? FALSE : has_colors());
7417
7418     if (use_colors) {
7419         start_color();
7420 #ifdef NCURSES_VERSION_PATCH
7421         max_colors = COLORS;    /* was > 16 ? 16 : COLORS */
7422 #if HAVE_USE_DEFAULT_COLORS
7423         if (default_colors) {
7424             use_default_colors();
7425             min_colors = -1;
7426         }
7427 #if HAVE_ASSUME_DEFAULT_COLORS
7428         if (assumed_colors)
7429             assume_default_colors(default_fg, default_bg);
7430 #endif
7431 #endif
7432 #else /* normal SVr4 curses */
7433         max_colors = COLORS;    /* was > 8 ? 8 : COLORS */
7434 #endif
7435         max_pairs = COLOR_PAIRS;        /* was > 256 ? 256 : COLOR_PAIRS */
7436
7437         if (can_change_color()) {
7438             init_all_colors(xterm_colors, palette_file);
7439         }
7440     }
7441
7442     /*
7443      * Return to terminal mode, so we're guaranteed of being able to
7444      * select terminal commands even if the capabilities are wrong.
7445      */
7446     endwin();
7447
7448 #if HAVE_CURSES_VERSION
7449     (void) printf("Welcome to %s.  Press ? for help.\n", curses_version());
7450 #elif defined(NCURSES_VERSION_MAJOR) && defined(NCURSES_VERSION_MINOR) && defined(NCURSES_VERSION_PATCH)
7451     (void) printf("Welcome to ncurses %d.%d.%d.  Press ? for help.\n",
7452                   NCURSES_VERSION_MAJOR,
7453                   NCURSES_VERSION_MINOR,
7454                   NCURSES_VERSION_PATCH);
7455 #else
7456     (void) puts("Welcome to ncurses.  Press ? for help.");
7457 #endif
7458
7459     main_menu(TRUE);
7460
7461     ExitProgram(EXIT_SUCCESS);
7462 }
7463
7464 /* ncurses.c ends here */