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