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