]> ncurses.scripts.mit.edu Git - ncurses.git/blob - test/ncurses.c
ncurses 5.9 - patch 20140621
[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.401 2014/06/21 18:37:29 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     if (has_colors()) {
2002         MvWPrintw(helpwin, row++, col,
2003                   "  c/C     cycle used-colors through 8,16,...,COLORS");
2004     }
2005     MvWPrintw(helpwin, row++, col,
2006               "  n/N     toggle text/number on/off");
2007     MvWPrintw(helpwin, row++, col,
2008               "  r/R     toggle reverse on/off");
2009     MvWPrintw(helpwin, row++, col,
2010               "  w/W     toggle width between 8/16 colors");
2011 #if USE_WIDEC_SUPPORT
2012     if (wide) {
2013         MvWPrintw(helpwin, row++, col,
2014                   "Wide characters:");
2015         MvWPrintw(helpwin, row, col,
2016                   "  x/X     toggle text between ASCII and wide-character");
2017     }
2018 #else
2019     (void) wide;
2020 #endif
2021 }
2022
2023 #define set_color_test(name, value) if (name != value) { name = value; base_row = 0; }
2024
2025 static int
2026 color_cycle(int current, int step)
2027 {
2028     int result = current;
2029     if (step < 0) {
2030         if (current <= 8) {
2031             result = COLORS;
2032         } else {
2033             result = 8;
2034             if ((result * 2) > COLORS) {
2035                 result = COLORS;
2036             } else {
2037                 while ((result * 2) < current) {
2038                     result *= 2;
2039                 }
2040             }
2041         }
2042     } else {
2043         if (current >= COLORS) {
2044             result = 8;
2045         } else {
2046             result *= 2;
2047         }
2048         if (result > COLORS)
2049             result = COLORS;
2050     }
2051     return result;
2052 }
2053
2054 /* generate a color test pattern */
2055 static void
2056 color_test(void)
2057 {
2058     NCURSES_PAIRS_T i;
2059     int top = 0, width;
2060     int base_row = 0;
2061     int grid_top = top + 3;
2062     int page_size = (LINES - grid_top);
2063     int pairs_max;
2064     int colors_max = COLORS;
2065     int row_limit;
2066     int per_row;
2067     char numbered[80];
2068     const char *hello;
2069     bool done = FALSE;
2070     bool opt_acsc = FALSE;
2071     bool opt_bold = FALSE;
2072     bool opt_revs = FALSE;
2073     bool opt_nums = FALSE;
2074     bool opt_wide = FALSE;
2075     WINDOW *helpwin;
2076
2077     while (!done) {
2078         int shown = 0;
2079
2080         pairs_max = PAIR_NUMBER(A_COLOR) + 1;
2081         if (colors_max * colors_max <= COLOR_PAIRS) {
2082             int limit = (colors_max - min_colors) * (colors_max - min_colors);
2083             if (pairs_max > limit)
2084                 pairs_max = limit;
2085         } else {
2086             if (pairs_max > COLOR_PAIRS)
2087                 pairs_max = COLOR_PAIRS;
2088         }
2089
2090         /* this assumes an 80-column line */
2091         if (opt_wide) {
2092             width = 4;
2093             hello = "Test";
2094             per_row = (colors_max > 8) ? 16 : 8;
2095         } else {
2096             width = 8;
2097             hello = "Hello";
2098             per_row = 8;
2099         }
2100         per_row -= min_colors;
2101
2102         row_limit = (pairs_max + per_row - 1) / per_row;
2103
2104         move(0, 0);
2105         (void) printw("There are %d color pairs and %d colors",
2106                       pairs_max, COLORS);
2107         if (colors_max != COLORS)
2108             (void) printw(" (using %d colors)", colors_max);
2109         if (min_colors)
2110             (void) addstr(" besides 'default'");
2111
2112         clrtobot();
2113         MvPrintw(top + 1, 0,
2114                  "%dx%d matrix of foreground/background colors, bold *%s*\n",
2115                  row_limit,
2116                  per_row,
2117                  opt_bold ? "on" : "off");
2118
2119         /* show color names/numbers across the top */
2120         for (i = 0; i < per_row; i++)
2121             show_color_name(top + 2, (i + 1) * width, i + min_colors, opt_wide);
2122
2123         /* show a grid of colors, with color names/ numbers on the left */
2124         for (i = (NCURSES_PAIRS_T) (base_row * per_row); i < pairs_max; i++) {
2125             int row = grid_top + (i / per_row) - base_row;
2126             int col = (i % per_row + 1) * width;
2127             NCURSES_PAIRS_T pair = i;
2128
2129 #define InxToFG(i) (NCURSES_COLOR_T) ((i % (colors_max - min_colors)) + min_colors)
2130 #define InxToBG(i) (NCURSES_COLOR_T) ((i / (colors_max - min_colors)) + min_colors)
2131             if (row >= 0 && move(row, col) != ERR) {
2132                 NCURSES_COLOR_T fg = InxToFG(i);
2133                 NCURSES_COLOR_T bg = InxToBG(i);
2134
2135                 init_pair(pair, fg, bg);
2136                 attron((attr_t) COLOR_PAIR(pair));
2137                 if (opt_acsc)
2138                     attron((attr_t) A_ALTCHARSET);
2139                 if (opt_bold)
2140                     attron((attr_t) A_BOLD);
2141                 if (opt_revs)
2142                     attron((attr_t) A_REVERSE);
2143
2144                 if (opt_nums) {
2145                     sprintf(numbered, "{%02X}", (int) i);
2146                     hello = numbered;
2147                 }
2148                 printw("%-*.*s", width, width, hello);
2149                 (void) attrset(A_NORMAL);
2150
2151                 if ((i % per_row) == 0 && InxToFG(i) == min_colors) {
2152                     show_color_name(row, 0, InxToBG(i), opt_wide);
2153                 }
2154                 ++shown;
2155             } else if (shown) {
2156                 break;
2157             }
2158         }
2159
2160         switch (wGetchar(stdscr)) {
2161         case 'a':
2162             opt_acsc = FALSE;
2163             break;
2164         case 'A':
2165             opt_acsc = TRUE;
2166             break;
2167         case 'b':
2168             opt_bold = FALSE;
2169             break;
2170         case 'B':
2171             opt_bold = TRUE;
2172             break;
2173         case 'c':
2174             colors_max = color_cycle(colors_max, -1);
2175             break;
2176         case 'C':
2177             colors_max = color_cycle(colors_max, 1);
2178             break;
2179         case 'n':
2180             opt_nums = FALSE;
2181             break;
2182         case 'N':
2183             opt_nums = TRUE;
2184             break;
2185         case 'r':
2186             opt_revs = FALSE;
2187             break;
2188         case 'R':
2189             opt_revs = TRUE;
2190             break;
2191         case case_QUIT:
2192             done = TRUE;
2193             continue;
2194         case 'w':
2195             set_color_test(opt_wide, FALSE);
2196             break;
2197         case 'W':
2198             set_color_test(opt_wide, TRUE);
2199             break;
2200         case CTRL('p'):
2201         case KEY_UP:
2202             if (base_row <= 0) {
2203                 beep();
2204             } else {
2205                 base_row -= 1;
2206             }
2207             break;
2208         case CTRL('n'):
2209         case KEY_DOWN:
2210             if (base_row + page_size >= row_limit) {
2211                 beep();
2212             } else {
2213                 base_row += 1;
2214             }
2215             break;
2216         case CTRL('b'):
2217         case KEY_PREVIOUS:
2218         case KEY_PPAGE:
2219             if (base_row <= 0) {
2220                 beep();
2221             } else {
2222                 base_row -= (page_size - 1);
2223                 if (base_row < 0)
2224                     base_row = 0;
2225             }
2226             break;
2227         case CTRL('f'):
2228         case KEY_NEXT:
2229         case KEY_NPAGE:
2230             if (base_row + page_size >= row_limit) {
2231                 beep();
2232             } else {
2233                 base_row += page_size - 1;
2234                 if (base_row + page_size >= row_limit) {
2235                     base_row = row_limit - page_size - 1;
2236                 }
2237             }
2238             break;
2239         case '?':
2240             if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
2241                 box(helpwin, 0, 0);
2242                 color_legend(helpwin, FALSE);
2243                 wGetchar(helpwin);
2244                 delwin(helpwin);
2245             }
2246             break;
2247         default:
2248             beep();
2249             continue;
2250         }
2251     }
2252
2253     erase();
2254     endwin();
2255 }
2256
2257 #if USE_WIDEC_SUPPORT
2258 /* generate a color test pattern */
2259 static void
2260 wide_color_test(void)
2261 {
2262     int i;
2263     int top = 0, width;
2264     int base_row = 0;
2265     int grid_top = top + 3;
2266     int page_size = (LINES - grid_top);
2267     int pairs_max = (unsigned short) (-1);
2268     int colors_max = COLORS;
2269     int row_limit;
2270     int per_row;
2271     char numbered[80];
2272     const char *hello;
2273     bool done = FALSE;
2274     bool opt_acsc = FALSE;
2275     bool opt_bold = FALSE;
2276     bool opt_revs = FALSE;
2277     bool opt_wide = FALSE;
2278     bool opt_nums = FALSE;
2279     bool opt_xchr = FALSE;
2280     wchar_t buffer[80];
2281     WINDOW *helpwin;
2282
2283     while (!done) {
2284         int shown = 0;
2285
2286         pairs_max = (unsigned short) (-1);
2287         if (colors_max * colors_max <= COLOR_PAIRS) {
2288             int limit = (colors_max - min_colors) * (colors_max - min_colors);
2289             if (pairs_max > limit)
2290                 pairs_max = limit;
2291         } else {
2292             if (pairs_max > COLOR_PAIRS)
2293                 pairs_max = COLOR_PAIRS;
2294         }
2295
2296         /* this assumes an 80-column line */
2297         if (opt_wide) {
2298             width = 4;
2299             hello = "Test";
2300             per_row = (colors_max > 8) ? 16 : 8;
2301         } else {
2302             width = 8;
2303             hello = "Hello";
2304             per_row = 8;
2305         }
2306         per_row -= min_colors;
2307
2308         if (opt_xchr) {
2309             make_fullwidth_text(buffer, hello);
2310             width *= 2;
2311             per_row /= 2;
2312         } else {
2313             make_narrow_text(buffer, hello);
2314         }
2315
2316         row_limit = (pairs_max + per_row - 1) / per_row;
2317
2318         move(0, 0);
2319         (void) printw("There are %d color pairs and %d colors",
2320                       pairs_max, COLORS);
2321         if (colors_max != COLORS)
2322             (void) printw(" (using %d colors)", colors_max);
2323         if (min_colors)
2324             (void) addstr(" besides 'default'");
2325
2326         clrtobot();
2327         MvPrintw(top + 1, 0,
2328                  "%dx%d matrix of foreground/background colors, bold *%s*\n",
2329                  row_limit,
2330                  per_row,
2331                  opt_bold ? "on" : "off");
2332
2333         /* show color names/numbers across the top */
2334         for (i = 0; i < per_row; i++)
2335             show_color_name(top + 2, (i + 1) * width, i + min_colors, opt_wide);
2336
2337         /* show a grid of colors, with color names/ numbers on the left */
2338         for (i = (base_row * per_row); i < pairs_max; i++) {
2339             int row = grid_top + (i / per_row) - base_row;
2340             int col = (i % per_row + 1) * width;
2341             NCURSES_PAIRS_T pair = (NCURSES_PAIRS_T) i;
2342
2343             if (row >= 0 && move(row, col) != ERR) {
2344                 init_pair(pair, InxToFG(i), InxToBG(i));
2345                 (void) color_set(pair, NULL);
2346                 if (opt_acsc)
2347                     attr_on((attr_t) A_ALTCHARSET, NULL);
2348                 if (opt_bold)
2349                     attr_on((attr_t) A_BOLD, NULL);
2350                 if (opt_revs)
2351                     attr_on((attr_t) A_REVERSE, NULL);
2352
2353                 if (opt_nums) {
2354                     sprintf(numbered, "{%02X}", i);
2355                     if (opt_xchr) {
2356                         make_fullwidth_text(buffer, numbered);
2357                     } else {
2358                         make_narrow_text(buffer, numbered);
2359                     }
2360                 }
2361                 addnwstr(buffer, width);
2362                 (void) attr_set(A_NORMAL, 0, NULL);
2363
2364                 if ((i % per_row) == 0 && InxToFG(i) == min_colors) {
2365                     show_color_name(row, 0, InxToBG(i), opt_wide);
2366                 }
2367                 ++shown;
2368             } else if (shown) {
2369                 break;
2370             }
2371         }
2372
2373         switch (wGetchar(stdscr)) {
2374         case 'a':
2375             opt_acsc = FALSE;
2376             break;
2377         case 'A':
2378             opt_acsc = TRUE;
2379             break;
2380         case 'b':
2381             opt_bold = FALSE;
2382             break;
2383         case 'B':
2384             opt_bold = TRUE;
2385             break;
2386         case 'c':
2387             colors_max = color_cycle(colors_max, -1);
2388             break;
2389         case 'C':
2390             colors_max = color_cycle(colors_max, 1);
2391             break;
2392         case 'n':
2393             opt_nums = FALSE;
2394             break;
2395         case 'N':
2396             opt_nums = TRUE;
2397             break;
2398         case 'r':
2399             opt_revs = FALSE;
2400             break;
2401         case 'R':
2402             opt_revs = TRUE;
2403             break;
2404         case case_QUIT:
2405             done = TRUE;
2406             continue;
2407         case 'w':
2408             set_color_test(opt_wide, FALSE);
2409             break;
2410         case 'W':
2411             set_color_test(opt_wide, TRUE);
2412             break;
2413         case 'x':
2414             opt_xchr = FALSE;
2415             break;
2416         case 'X':
2417             opt_xchr = TRUE;
2418             break;
2419         case CTRL('p'):
2420         case KEY_UP:
2421             if (base_row <= 0) {
2422                 beep();
2423             } else {
2424                 base_row -= 1;
2425             }
2426             break;
2427         case CTRL('n'):
2428         case KEY_DOWN:
2429             if (base_row + page_size >= row_limit) {
2430                 beep();
2431             } else {
2432                 base_row += 1;
2433             }
2434             break;
2435         case CTRL('b'):
2436         case KEY_PREVIOUS:
2437         case KEY_PPAGE:
2438             if (base_row <= 0) {
2439                 beep();
2440             } else {
2441                 base_row -= (page_size - 1);
2442                 if (base_row < 0)
2443                     base_row = 0;
2444             }
2445             break;
2446         case CTRL('f'):
2447         case KEY_NEXT:
2448         case KEY_NPAGE:
2449             if (base_row + page_size >= row_limit) {
2450                 beep();
2451             } else {
2452                 base_row += page_size - 1;
2453                 if (base_row + page_size >= row_limit) {
2454                     base_row = row_limit - page_size - 1;
2455                 }
2456             }
2457             break;
2458         case '?':
2459             if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
2460                 box(helpwin, 0, 0);
2461                 color_legend(helpwin, TRUE);
2462                 wGetchar(helpwin);
2463                 delwin(helpwin);
2464             }
2465             break;
2466         default:
2467             beep();
2468             continue;
2469         }
2470     }
2471
2472     erase();
2473     endwin();
2474 }
2475 #endif /* USE_WIDEC_SUPPORT */
2476
2477 static void
2478 change_color(NCURSES_PAIRS_T current, int field, int value, int usebase)
2479 {
2480     NCURSES_COLOR_T red, green, blue;
2481
2482     color_content(current, &red, &green, &blue);
2483
2484     switch (field) {
2485     case 0:
2486         red = (NCURSES_COLOR_T) (usebase ? (red + value) : value);
2487         break;
2488     case 1:
2489         green = (NCURSES_COLOR_T) (usebase ? (green + value) : value);
2490         break;
2491     case 2:
2492         blue = (NCURSES_COLOR_T) (usebase ? (blue + value) : value);
2493         break;
2494     }
2495
2496     if (init_color(current, red, green, blue) == ERR)
2497         beep();
2498 }
2499
2500 static void
2501 init_all_colors(void)
2502 {
2503     NCURSES_PAIRS_T c;
2504
2505     for (c = 0; c < COLORS; ++c)
2506         init_color(c,
2507                    all_colors[c].red,
2508                    all_colors[c].green,
2509                    all_colors[c].blue);
2510 }
2511
2512 #define scaled_rgb(n) ((255 * (n)) / 1000)
2513
2514 static void
2515 color_edit(void)
2516 /* display the color test pattern, without trying to edit colors */
2517 {
2518     int i;
2519     int current = 0;
2520     int this_c = 0, value = 0, field = 0;
2521     int last_c;
2522     int top_color = 0;
2523     int page_size = (LINES - 6);
2524
2525     init_all_colors();
2526     refresh();
2527
2528     for (i = 0; i < max_colors; i++)
2529         init_pair((NCURSES_PAIRS_T) i,
2530                   (NCURSES_COLOR_T) COLOR_WHITE,
2531                   (NCURSES_COLOR_T) i);
2532
2533     MvPrintw(LINES - 2, 0, "Number: %d", value);
2534
2535     do {
2536         NCURSES_COLOR_T red, green, blue;
2537
2538         attron(A_BOLD);
2539         MvAddStr(0, 20, "Color RGB Value Editing");
2540         attroff(A_BOLD);
2541
2542         for (i = (NCURSES_COLOR_T) top_color;
2543              (i - top_color < page_size)
2544              && (i < max_colors); i++) {
2545             char numeric[80];
2546
2547             sprintf(numeric, "[%d]", i);
2548             MvPrintw(2 + i - top_color, 0, "%c %-8s:",
2549                      (i == current ? '>' : ' '),
2550                      (i < (int) SIZEOF(the_color_names)
2551                       ? the_color_names[i] : numeric));
2552             (void) attrset((attr_t) COLOR_PAIR(i));
2553             addstr("        ");
2554             (void) attrset(A_NORMAL);
2555
2556             color_content((NCURSES_PAIRS_T) i, &red, &green, &blue);
2557             addstr("   R = ");
2558             if (current == i && field == 0)
2559                 attron(A_STANDOUT);
2560             printw("%04d", (int) red);
2561             if (current == i && field == 0)
2562                 (void) attrset(A_NORMAL);
2563             addstr(", G = ");
2564             if (current == i && field == 1)
2565                 attron(A_STANDOUT);
2566             printw("%04d", (int) green);
2567             if (current == i && field == 1)
2568                 (void) attrset(A_NORMAL);
2569             addstr(", B = ");
2570             if (current == i && field == 2)
2571                 attron(A_STANDOUT);
2572             printw("%04d", (int) blue);
2573             if (current == i && field == 2)
2574                 (void) attrset(A_NORMAL);
2575             (void) attrset(A_NORMAL);
2576             printw(" ( %3d %3d %3d )",
2577                    (int) scaled_rgb(red),
2578                    (int) scaled_rgb(green),
2579                    (int) scaled_rgb(blue));
2580         }
2581
2582         MvAddStr(LINES - 3, 0,
2583                  "Use up/down to select a color, left/right to change fields.");
2584         MvAddStr(LINES - 2, 0,
2585                  "Modify field by typing nnn=, nnn-, or nnn+.  ? for help.");
2586
2587         move(2 + current - top_color, 0);
2588
2589         last_c = this_c;
2590         this_c = Getchar();
2591         if (this_c < 256 && isdigit(this_c) && !isdigit(last_c))
2592             value = 0;
2593
2594         switch (this_c) {
2595         case CTRL('b'):
2596         case KEY_PPAGE:
2597             if (current > 0)
2598                 current -= (page_size - 1);
2599             else
2600                 beep();
2601             break;
2602
2603         case CTRL('f'):
2604         case KEY_NPAGE:
2605             if (current < (max_colors - 1))
2606                 current += (page_size - 1);
2607             else
2608                 beep();
2609             break;
2610
2611         case CTRL('p'):
2612         case KEY_UP:
2613             current = (current == 0 ? (max_colors - 1) : current - 1);
2614             break;
2615
2616         case CTRL('n'):
2617         case KEY_DOWN:
2618             current = (current == (max_colors - 1) ? 0 : current + 1);
2619             break;
2620
2621         case KEY_RIGHT:
2622             field = (field == 2 ? 0 : field + 1);
2623             break;
2624
2625         case KEY_LEFT:
2626             field = (field == 0 ? 2 : field - 1);
2627             break;
2628
2629         case '0':
2630         case '1':
2631         case '2':
2632         case '3':
2633         case '4':
2634         case '5':
2635         case '6':
2636         case '7':
2637         case '8':
2638         case '9':
2639             value = value * 10 + (this_c - '0');
2640             break;
2641
2642         case '+':
2643             change_color((NCURSES_PAIRS_T) current, field, value, 1);
2644             break;
2645
2646         case '-':
2647             change_color((NCURSES_PAIRS_T) current, field, -value, 1);
2648             break;
2649
2650         case '=':
2651             change_color((NCURSES_PAIRS_T) current, field, value, 0);
2652             break;
2653
2654         case '?':
2655             erase();
2656             P("                      RGB Value Editing Help");
2657             P("");
2658             P("You are in the RGB value editor.  Use the arrow keys to select one of");
2659             P("the fields in one of the RGB triples of the current colors; the one");
2660             P("currently selected will be reverse-video highlighted.");
2661             P("");
2662             P("To change a field, enter the digits of the new value; they are echoed");
2663             P("as entered.  Finish by typing `='.  The change will take effect instantly.");
2664             P("To increment or decrement a value, use the same procedure, but finish");
2665             P("with a `+' or `-'.");
2666             P("");
2667             P("Press 'm' to invoke the top-level menu with the current color settings.");
2668             P("To quit, do ESC");
2669
2670             Pause();
2671             erase();
2672             break;
2673
2674         case 'm':
2675             endwin();
2676             main_menu(FALSE);
2677             for (i = 0; i < max_colors; i++)
2678                 init_pair((NCURSES_PAIRS_T) i,
2679                           (NCURSES_COLOR_T) COLOR_WHITE,
2680                           (NCURSES_COLOR_T) i);
2681             refresh();
2682             break;
2683
2684         case case_QUIT:
2685             break;
2686
2687         default:
2688             beep();
2689             break;
2690         }
2691
2692         if (current < 0)
2693             current = 0;
2694         if (current >= max_colors)
2695             current = max_colors - 1;
2696         if (current < top_color)
2697             top_color = current;
2698         if (current - top_color >= page_size)
2699             top_color = current - (page_size - 1);
2700
2701         MvPrintw(LINES - 1, 0, "Number: %d", value);
2702         clrtoeol();
2703     } while
2704         (!isQuit(this_c));
2705
2706     erase();
2707
2708     /*
2709      * ncurses does not reset each color individually when calling endwin().
2710      */
2711     init_all_colors();
2712
2713     endwin();
2714 }
2715
2716 /****************************************************************************
2717  *
2718  * Alternate character-set stuff
2719  *
2720  ****************************************************************************/
2721 static bool
2722 cycle_attr(int ch, unsigned *at_code, chtype *attr, ATTR_TBL * list, unsigned limit)
2723 {
2724     bool result = TRUE;
2725
2726     switch (ch) {
2727     case 'v':
2728         if ((*at_code += 1) >= limit)
2729             *at_code = 0;
2730         break;
2731     case 'V':
2732         if (*at_code == 0)
2733             *at_code = limit - 1;
2734         else
2735             *at_code -= 1;
2736         break;
2737     default:
2738         result = FALSE;
2739         break;
2740     }
2741     if (result)
2742         *attr = list[*at_code].attr;
2743     return result;
2744 }
2745
2746 static bool
2747 cycle_colors(int ch, int *fg, int *bg, NCURSES_PAIRS_T *pair)
2748 {
2749     bool result = FALSE;
2750
2751     if (use_colors) {
2752         result = TRUE;
2753         switch (ch) {
2754         case 'F':
2755             if ((*fg -= 1) < 0)
2756                 *fg = COLORS - 1;
2757             break;
2758         case 'f':
2759             if ((*fg += 1) >= COLORS)
2760                 *fg = 0;
2761             break;
2762         case 'B':
2763             if ((*bg -= 1) < 0)
2764                 *bg = COLORS - 1;
2765             break;
2766         case 'b':
2767             if ((*bg += 1) >= COLORS)
2768                 *bg = 0;
2769             break;
2770         default:
2771             result = FALSE;
2772             break;
2773         }
2774         if (result) {
2775             *pair = (NCURSES_PAIRS_T) (*fg != COLOR_BLACK || *bg != COLOR_BLACK);
2776             if (*pair != 0) {
2777                 *pair = 1;
2778                 if (init_pair(*pair,
2779                               (NCURSES_COLOR_T) *fg,
2780                               (NCURSES_COLOR_T) *bg) == ERR) {
2781                     result = FALSE;
2782                 }
2783             }
2784         }
2785     }
2786     return result;
2787 }
2788
2789 /****************************************************************************
2790  *
2791  * Soft-key label test
2792  *
2793  ****************************************************************************/
2794
2795 #if USE_SOFTKEYS
2796
2797 #define SLK_HELP 17
2798 #define SLK_WORK (SLK_HELP + 3)
2799
2800 static void
2801 slk_help(void)
2802 {
2803     static const char *table[] =
2804     {
2805         "Available commands are:"
2806         ,""
2807         ,"^L         -- repaint this message and activate soft keys"
2808         ,"a/d        -- activate/disable soft keys"
2809         ,"c          -- set centered format for labels"
2810         ,"l          -- set left-justified format for labels"
2811         ,"r          -- set right-justified format for labels"
2812         ,"[12345678] -- set label; labels are numbered 1 through 8"
2813         ,"e          -- erase stdscr (should not erase labels)"
2814         ,"s          -- test scrolling of shortened screen"
2815         ,"v/V        -- cycle through video attributes"
2816 #if HAVE_SLK_COLOR
2817         ,"F/f/B/b    -- cycle through foreground/background colors"
2818 #endif
2819         ,"ESC        -- return to main menu"
2820         ,""
2821         ,"Note: if activating the soft keys causes your terminal to scroll up"
2822         ,"one line, your terminal auto-scrolls when anything is written to the"
2823         ,"last screen position.  The ncurses code does not yet handle this"
2824         ,"gracefully."
2825     };
2826     unsigned j;
2827
2828     move(2, 0);
2829     for (j = 0; j < SIZEOF(table); ++j) {
2830         P(table[j]);
2831     }
2832     refresh();
2833 }
2834
2835 #if HAVE_SLK_COLOR
2836 static void
2837 call_slk_color(int fg, int bg)
2838 {
2839     init_pair(1, (NCURSES_COLOR_T) bg, (NCURSES_COLOR_T) fg);
2840     slk_color(1);
2841     MvPrintw(SLK_WORK, 0, "Colors %d/%d\n", fg, bg);
2842     clrtoeol();
2843     slk_touch();
2844     slk_noutrefresh();
2845     refresh();
2846 }
2847 #endif
2848
2849 static void
2850 slk_test(void)
2851 /* exercise the soft keys */
2852 {
2853     int c, fmt = 1;
2854     char buf[9];
2855     char *s;
2856     chtype attr = A_NORMAL;
2857     unsigned at_code = 0;
2858 #if HAVE_SLK_COLOR
2859     int fg = COLOR_BLACK;
2860     int bg = COLOR_WHITE;
2861     NCURSES_PAIRS_T pair = 0;
2862 #endif
2863     ATTR_TBL my_list[SIZEOF(attrs_to_test)];
2864     unsigned my_size = init_attr_list(my_list, termattrs());
2865
2866     c = CTRL('l');
2867 #if HAVE_SLK_COLOR
2868     if (use_colors) {
2869         call_slk_color(fg, bg);
2870     }
2871 #endif
2872
2873     do {
2874         move(0, 0);
2875         switch (c) {
2876         case CTRL('l'):
2877             erase();
2878             attron(A_BOLD);
2879             MvAddStr(0, 20, "Soft Key Exerciser");
2880             attroff(A_BOLD);
2881
2882             slk_help();
2883             /* fall through */
2884
2885         case 'a':
2886             slk_restore();
2887             break;
2888
2889         case 'e':
2890             wclear(stdscr);
2891             break;
2892
2893         case 's':
2894             MvPrintw(SLK_WORK, 0, "Press Q to stop the scrolling-test: ");
2895             while ((c = Getchar()) != 'Q' && (c != ERR))
2896                 addch((chtype) c);
2897             break;
2898
2899         case 'd':
2900             slk_clear();
2901             break;
2902
2903         case 'l':
2904             fmt = 0;
2905             break;
2906
2907         case 'c':
2908             fmt = 1;
2909             break;
2910
2911         case 'r':
2912             fmt = 2;
2913             break;
2914
2915         case '1':
2916         case '2':
2917         case '3':
2918         case '4':
2919         case '5':
2920         case '6':
2921         case '7':
2922         case '8':
2923             MvAddStr(SLK_WORK, 0, "Please enter the label value: ");
2924             strcpy(buf, "");
2925             if ((s = slk_label(c - '0')) != 0) {
2926                 strncpy(buf, s, (size_t) 8);
2927             }
2928             wGetstring(stdscr, buf, 8);
2929             slk_set((c - '0'), buf, fmt);
2930             slk_refresh();
2931             move(SLK_WORK, 0);
2932             clrtobot();
2933             break;
2934
2935         case case_QUIT:
2936             goto done;
2937
2938 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
2939         case KEY_RESIZE:
2940             wnoutrefresh(stdscr);
2941             break;
2942 #endif
2943
2944         default:
2945             if (cycle_attr(c, &at_code, &attr, my_list, my_size)) {
2946                 slk_attrset(attr);
2947                 slk_touch();
2948                 slk_noutrefresh();
2949                 break;
2950             }
2951 #if HAVE_SLK_COLOR
2952             if (cycle_colors(c, &fg, &bg, &pair)) {
2953                 if (use_colors) {
2954                     call_slk_color(fg, bg);
2955                 } else {
2956                     beep();
2957                 }
2958                 break;
2959             }
2960 #endif
2961             beep();
2962             break;
2963         }
2964     } while (!isQuit(c = Getchar()));
2965
2966   done:
2967     slk_clear();
2968     erase();
2969     endwin();
2970 }
2971
2972 #if USE_WIDEC_SUPPORT
2973 #define SLKLEN 8
2974 static void
2975 wide_slk_test(void)
2976 /* exercise the soft keys */
2977 {
2978     int c, fmt = 1;
2979     wchar_t buf[SLKLEN + 1];
2980     char *s;
2981     chtype attr = A_NORMAL;
2982     unsigned at_code = 0;
2983     int fg = COLOR_BLACK;
2984     int bg = COLOR_WHITE;
2985     NCURSES_PAIRS_T pair = 0;
2986     ATTR_TBL my_list[SIZEOF(attrs_to_test)];
2987     unsigned my_size = init_attr_list(my_list, term_attrs());
2988
2989     c = CTRL('l');
2990     if (use_colors) {
2991         call_slk_color(fg, bg);
2992     }
2993     do {
2994         move(0, 0);
2995         switch (c) {
2996         case CTRL('l'):
2997             erase();
2998             attr_on(WA_BOLD, NULL);
2999             MvAddStr(0, 20, "Soft Key Exerciser");
3000             attr_off(WA_BOLD, NULL);
3001
3002             slk_help();
3003             /* fall through */
3004
3005         case 'a':
3006             slk_restore();
3007             break;
3008
3009         case 'e':
3010             wclear(stdscr);
3011             break;
3012
3013         case 's':
3014             MvPrintw(SLK_WORK, 0, "Press Q to stop the scrolling-test: ");
3015             while ((c = Getchar()) != 'Q' && (c != ERR))
3016                 addch((chtype) c);
3017             break;
3018
3019         case 'd':
3020             slk_clear();
3021             break;
3022
3023         case 'l':
3024             fmt = 0;
3025             break;
3026
3027         case 'c':
3028             fmt = 1;
3029             break;
3030
3031         case 'r':
3032             fmt = 2;
3033             break;
3034
3035         case '1':
3036         case '2':
3037         case '3':
3038         case '4':
3039         case '5':
3040         case '6':
3041         case '7':
3042         case '8':
3043             MvAddStr(SLK_WORK, 0, "Please enter the label value: ");
3044             *buf = 0;
3045             if ((s = slk_label(c - '0')) != 0) {
3046                 char *temp = strdup(s);
3047                 size_t used = strlen(temp);
3048                 size_t want = SLKLEN;
3049                 size_t test;
3050 #ifndef state_unused
3051                 mbstate_t state;
3052 #endif
3053
3054                 buf[0] = L'\0';
3055                 while (want > 0 && used != 0) {
3056                     const char *base = s;
3057                     reset_mbytes(state);
3058                     test = count_mbytes(base, 0, &state);
3059                     if (test == (size_t) -1) {
3060                         temp[--used] = 0;
3061                     } else if (test > want) {
3062                         temp[--used] = 0;
3063                     } else {
3064                         reset_mbytes(state);
3065                         trans_mbytes(buf, base, want, &state);
3066                         break;
3067                     }
3068                 }
3069                 free(temp);
3070             }
3071             wGet_wstring(stdscr, buf, SLKLEN);
3072             slk_wset((c - '0'), buf, fmt);
3073             slk_refresh();
3074             move(SLK_WORK, 0);
3075             clrtobot();
3076             break;
3077
3078         case case_QUIT:
3079             goto done;
3080
3081         case 'F':
3082             if (use_colors) {
3083                 fg = (NCURSES_COLOR_T) ((fg + 1) % COLORS);
3084                 call_slk_color(fg, bg);
3085             }
3086             break;
3087         case 'B':
3088             if (use_colors) {
3089                 bg = (NCURSES_COLOR_T) ((bg + 1) % COLORS);
3090                 call_slk_color(fg, bg);
3091             }
3092             break;
3093 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
3094         case KEY_RESIZE:
3095             wnoutrefresh(stdscr);
3096             break;
3097 #endif
3098         default:
3099             if (cycle_attr(c, &at_code, &attr, my_list, my_size)) {
3100                 slk_attr_set(attr, (NCURSES_COLOR_T) (fg || bg), NULL);
3101                 slk_touch();
3102                 slk_noutrefresh();
3103                 break;
3104             }
3105 #if HAVE_SLK_COLOR
3106             if (cycle_colors(c, &fg, &bg, &pair)) {
3107                 if (use_colors) {
3108                     call_slk_color(fg, bg);
3109                 } else {
3110                     beep();
3111                 }
3112                 break;
3113             }
3114 #endif
3115             beep();
3116             break;
3117         }
3118     } while (!isQuit(c = Getchar()));
3119
3120   done:
3121     slk_clear();
3122     erase();
3123     endwin();
3124 }
3125 #endif
3126 #endif /* SLK_INIT */
3127
3128 static void
3129 show_256_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3130 {
3131     unsigned first = 0;
3132     unsigned last = 255;
3133     unsigned code;
3134     int count;
3135
3136     erase();
3137     attron(A_BOLD);
3138     MvPrintw(0, 20, "Display of Character Codes %#0x to %#0x",
3139              first, last);
3140     attroff(A_BOLD);
3141     refresh();
3142
3143     for (code = first; code <= last; ++code) {
3144         int row = (int) (2 + (code / 16));
3145         int col = (int) (5 * (code % 16));
3146         IGNORE_RC(mvaddch(row, col, colored_chtype(code, attr, pair)));
3147         for (count = 1; count < repeat; ++count) {
3148             addch(colored_chtype(code, attr, pair));
3149         }
3150     }
3151
3152 }
3153
3154 /*
3155  * Show a slice of 32 characters, allowing those to be repeated up to the
3156  * screen's width.
3157  *
3158  * ISO 6429:  codes 0x80 to 0x9f may be control characters that cause the
3159  * terminal to perform functions.  The remaining codes can be graphic.
3160  */
3161 static void
3162 show_upper_chars(int base, int pagesize, int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3163 {
3164     unsigned code;
3165     unsigned first = (unsigned) base;
3166     unsigned last = first + (unsigned) pagesize - 2;
3167     bool C1 = (first == 128);
3168     int reply;
3169
3170     erase();
3171     attron(A_BOLD);
3172     MvPrintw(0, 20, "Display of %s Character Codes %d to %d",
3173              C1 ? "C1" : "GR", first, last);
3174     attroff(A_BOLD);
3175     refresh();
3176
3177     for (code = first; code <= last; code++) {
3178         int count = repeat;
3179         int row = 2 + ((int) (code - first) % (pagesize / 2));
3180         int col = ((int) (code - first) / (pagesize / 2)) * COLS / 2;
3181         char tmp[80];
3182         sprintf(tmp, "%3u (0x%x)", code, code);
3183         MvPrintw(row, col, "%*s: ", COLS / 4, tmp);
3184
3185         do {
3186             if (C1)
3187                 nodelay(stdscr, TRUE);
3188             echochar(colored_chtype(code, attr, pair));
3189             if (C1) {
3190                 /* (yes, this _is_ crude) */
3191                 while ((reply = Getchar()) != ERR) {
3192                     addch(UChar(reply));
3193                     napms(10);
3194                 }
3195                 nodelay(stdscr, FALSE);
3196             }
3197         } while (--count > 0);
3198     }
3199 }
3200
3201 #define PC_COLS 4
3202
3203 static void
3204 show_pc_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3205 {
3206     unsigned code;
3207
3208     erase();
3209     attron(A_BOLD);
3210     MvPrintw(0, 20, "Display of PC Character Codes");
3211     attroff(A_BOLD);
3212     refresh();
3213
3214     for (code = 0; code < 16; ++code) {
3215         MvPrintw(2, (int) code * PC_COLS + 8, "%X", code);
3216     }
3217     for (code = 0; code < 256; code++) {
3218         int count = repeat;
3219         int row = 3 + (int) (code / 16) + (code >= 128);
3220         int col = 8 + (int) (code % 16) * PC_COLS;
3221         if ((code % 16) == 0)
3222             MvPrintw(row, 0, "0x%02x:", code);
3223         move(row, col);
3224         do {
3225             switch (code) {
3226             case '\n':
3227             case '\r':
3228             case '\b':
3229             case '\f':
3230             case '\033':
3231             case 0x9b:
3232                 /*
3233                  * Skip the ones that do not work.
3234                  */
3235                 break;
3236             default:
3237                 addch(colored_chtype(code, A_ALTCHARSET | attr, pair));
3238                 break;
3239             }
3240         } while (--count > 0);
3241     }
3242 }
3243
3244 static void
3245 show_box_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3246 {
3247     (void) repeat;
3248
3249     attr |= (attr_t) COLOR_PAIR(pair);
3250
3251     erase();
3252     attron(A_BOLD);
3253     MvAddStr(0, 20, "Display of the ACS Line-Drawing Set");
3254     attroff(A_BOLD);
3255     refresh();
3256     /* *INDENT-OFF* */
3257     wborder(stdscr,
3258             colored_chtype(ACS_VLINE,    attr, pair),
3259             colored_chtype(ACS_VLINE,    attr, pair),
3260             colored_chtype(ACS_HLINE,    attr, pair),
3261             colored_chtype(ACS_HLINE,    attr, pair),
3262             colored_chtype(ACS_ULCORNER, attr, pair),
3263             colored_chtype(ACS_URCORNER, attr, pair),
3264             colored_chtype(ACS_LLCORNER, attr, pair),
3265             colored_chtype(ACS_LRCORNER, attr, pair));
3266     MvHLine(LINES / 2, 0,        colored_chtype(ACS_HLINE, attr, pair), COLS);
3267     MvVLine(0,         COLS / 2, colored_chtype(ACS_VLINE, attr, pair), LINES);
3268     MvAddCh(0,         COLS / 2, colored_chtype(ACS_TTEE,  attr, pair));
3269     MvAddCh(LINES / 2, COLS / 2, colored_chtype(ACS_PLUS,  attr, pair));
3270     MvAddCh(LINES - 1, COLS / 2, colored_chtype(ACS_BTEE,  attr, pair));
3271     MvAddCh(LINES / 2, 0,        colored_chtype(ACS_LTEE,  attr, pair));
3272     MvAddCh(LINES / 2, COLS - 1, colored_chtype(ACS_RTEE,  attr, pair));
3273     /* *INDENT-ON* */
3274
3275 }
3276
3277 static int
3278 show_1_acs(int n, int repeat, const char *name, chtype code)
3279 {
3280     const int height = 16;
3281     int row = 2 + (n % height);
3282     int col = (n / height) * COLS / 2;
3283
3284     MvPrintw(row, col, "%*s : ", COLS / 4, name);
3285     do {
3286         addch(code);
3287     } while (--repeat > 0);
3288     return n + 1;
3289 }
3290
3291 static void
3292 show_acs_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3293 /* display the ACS character set */
3294 {
3295     int n;
3296
3297 #define BOTH(name) #name, colored_chtype(name, attr, (chtype) pair)
3298
3299     erase();
3300     attron(A_BOLD);
3301     MvAddStr(0, 20, "Display of the ACS Character Set");
3302     attroff(A_BOLD);
3303     refresh();
3304
3305     n = show_1_acs(0, repeat, BOTH(ACS_ULCORNER));
3306     n = show_1_acs(n, repeat, BOTH(ACS_URCORNER));
3307     n = show_1_acs(n, repeat, BOTH(ACS_LLCORNER));
3308     n = show_1_acs(n, repeat, BOTH(ACS_LRCORNER));
3309
3310     n = show_1_acs(n, repeat, BOTH(ACS_LTEE));
3311     n = show_1_acs(n, repeat, BOTH(ACS_RTEE));
3312     n = show_1_acs(n, repeat, BOTH(ACS_TTEE));
3313     n = show_1_acs(n, repeat, BOTH(ACS_BTEE));
3314
3315     n = show_1_acs(n, repeat, BOTH(ACS_HLINE));
3316     n = show_1_acs(n, repeat, BOTH(ACS_VLINE));
3317
3318     /*
3319      * HPUX's ACS definitions are broken here.  Just give up.
3320      */
3321 #if !(defined(__hpux) && !defined(NCURSES_VERSION))
3322     n = show_1_acs(n, repeat, BOTH(ACS_LARROW));
3323     n = show_1_acs(n, repeat, BOTH(ACS_RARROW));
3324     n = show_1_acs(n, repeat, BOTH(ACS_UARROW));
3325     n = show_1_acs(n, repeat, BOTH(ACS_DARROW));
3326
3327     n = show_1_acs(n, repeat, BOTH(ACS_BLOCK));
3328     n = show_1_acs(n, repeat, BOTH(ACS_BOARD));
3329     n = show_1_acs(n, repeat, BOTH(ACS_LANTERN));
3330     n = show_1_acs(n, repeat, BOTH(ACS_BULLET));
3331     n = show_1_acs(n, repeat, BOTH(ACS_CKBOARD));
3332     n = show_1_acs(n, repeat, BOTH(ACS_DEGREE));
3333     n = show_1_acs(n, repeat, BOTH(ACS_DIAMOND));
3334     n = show_1_acs(n, repeat, BOTH(ACS_PLMINUS));
3335     n = show_1_acs(n, repeat, BOTH(ACS_PLUS));
3336
3337     n = show_1_acs(n, repeat, BOTH(ACS_GEQUAL));
3338     n = show_1_acs(n, repeat, BOTH(ACS_NEQUAL));
3339     n = show_1_acs(n, repeat, BOTH(ACS_LEQUAL));
3340
3341     n = show_1_acs(n, repeat, BOTH(ACS_STERLING));
3342     n = show_1_acs(n, repeat, BOTH(ACS_PI));
3343     n = show_1_acs(n, repeat, BOTH(ACS_S1));
3344     n = show_1_acs(n, repeat, BOTH(ACS_S3));
3345     n = show_1_acs(n, repeat, BOTH(ACS_S7));
3346     (void) show_1_acs(n, repeat, BOTH(ACS_S9));
3347 #endif
3348 }
3349
3350 static void
3351 acs_display(void)
3352 {
3353     int c = 'a';
3354     int pagesize = 32;
3355     char *term = getenv("TERM");
3356     const char *pch_kludge = ((term != 0 && strstr(term, "linux"))
3357                               ? "p=PC, "
3358                               : "");
3359     chtype attr = A_NORMAL;
3360     int digit = 0;
3361     int repeat = 1;
3362     int fg = COLOR_BLACK;
3363     int bg = COLOR_BLACK;
3364     unsigned at_code = 0;
3365     NCURSES_PAIRS_T pair = 0;
3366     void (*last_show_acs) (int, attr_t, NCURSES_PAIRS_T) = 0;
3367     ATTR_TBL my_list[SIZEOF(attrs_to_test)];
3368     unsigned my_size = init_attr_list(my_list, termattrs());
3369
3370     do {
3371         switch (c) {
3372         case CTRL('L'):
3373             Repaint();
3374             break;
3375         case 'a':
3376             ToggleAcs(last_show_acs, show_acs_chars);
3377             break;
3378         case 'p':
3379             if (*pch_kludge)
3380                 ToggleAcs(last_show_acs, show_pc_chars);
3381             else
3382                 beep();
3383             break;
3384         case 'w':
3385             if (pagesize == 32) {
3386                 pagesize = 256;
3387             } else {
3388                 pagesize = 32;
3389             }
3390             break;
3391         case 'x':
3392             ToggleAcs(last_show_acs, show_box_chars);
3393             break;
3394         case '0':
3395         case '1':
3396         case '2':
3397         case '3':
3398             digit = (c - '0');
3399             last_show_acs = 0;
3400             break;
3401         case '-':
3402             if (digit > 0) {
3403                 --digit;
3404                 last_show_acs = 0;
3405             } else {
3406                 beep();
3407             }
3408             break;
3409         case '+':
3410             if (digit < 3) {
3411                 ++digit;
3412                 last_show_acs = 0;
3413             } else {
3414                 beep();
3415             }
3416             break;
3417         case '>':
3418             if (repeat < (COLS / 4))
3419                 ++repeat;
3420             break;
3421         case '<':
3422             if (repeat > 1)
3423                 --repeat;
3424             break;
3425         default:
3426             if (cycle_attr(c, &at_code, &attr, my_list, my_size)
3427                 || cycle_colors(c, &fg, &bg, &pair)) {
3428                 break;
3429             } else {
3430                 beep();
3431             }
3432             break;
3433         }
3434         if (pagesize != 32) {
3435             show_256_chars(repeat, attr, pair);
3436         } else if (last_show_acs != 0) {
3437             last_show_acs(repeat, attr, pair);
3438         } else {
3439             show_upper_chars(digit * pagesize + 128, pagesize, repeat, attr, pair);
3440         }
3441
3442         MvPrintw(LINES - 3, 0,
3443                  "Note: ANSI terminals may not display C1 characters.");
3444         MvPrintw(LINES - 2, 0,
3445                  "Select: a=ACS, w=all x=box, %s0=C1, 1-3,+/- non-ASCII, </> repeat, ESC=quit",
3446                  pch_kludge);
3447         if (use_colors) {
3448             MvPrintw(LINES - 1, 0,
3449                      "v/V, f/F, b/B cycle through video attributes (%s) and color %d/%d.",
3450                      my_list[at_code].name,
3451                      fg, bg);
3452         } else {
3453             MvPrintw(LINES - 1, 0,
3454                      "v/V cycles through video attributes (%s).",
3455                      my_list[at_code].name);
3456         }
3457         refresh();
3458     } while (!isQuit(c = Getchar()));
3459
3460     Pause();
3461     erase();
3462     endwin();
3463 }
3464
3465 #if USE_WIDEC_SUPPORT
3466 static cchar_t *
3467 merge_wide_attr(cchar_t *dst, const cchar_t *src, attr_t attr, NCURSES_PAIRS_T pair)
3468 {
3469     int count;
3470
3471     *dst = *src;
3472     do {
3473         TEST_CCHAR(src, count, {
3474             attr |= (test_attrs & A_ALTCHARSET);
3475             setcchar(dst, test_wch, attr, pair, NULL);
3476         }
3477         , {
3478             ;
3479         });
3480     } while (0);
3481     return dst;
3482 }
3483
3484 /*
3485  * Header/legend take up no more than 8 lines, leaving 16 lines on a 24-line
3486  * display.  If there are no repeats, we could normally display 16 lines of 64
3487  * characters (1024 total).  However, taking repeats and double-width cells
3488  * into account, use 256 characters for the page.
3489  */
3490 static void
3491 show_paged_widechars(int base,
3492                      int pagesize,
3493                      int repeat,
3494                      int space,
3495                      attr_t attr,
3496                      NCURSES_PAIRS_T pair)
3497 {
3498     int first = base * pagesize;
3499     int last = first + pagesize - 1;
3500     int per_line = 16;
3501     cchar_t temp;
3502     wchar_t code;
3503     wchar_t codes[10];
3504
3505     erase();
3506     attron(A_BOLD);
3507     MvPrintw(0, 20, "Display of Character Codes %#x to %#x", first, last);
3508     attroff(A_BOLD);
3509
3510     for (code = first; (int) code <= last; code++) {
3511         int row = (2 + ((int) code - first) / per_line);
3512         int col = 5 * ((int) code % per_line);
3513         int count;
3514
3515         memset(&codes, 0, sizeof(codes));
3516         codes[0] = code;
3517         setcchar(&temp, codes, attr, pair, 0);
3518         move(row, col);
3519         if (wcwidth(code) == 0 && code != 0) {
3520             addch((chtype) space |
3521                   (A_REVERSE ^ attr) |
3522                   (attr_t) COLOR_PAIR(pair));
3523         }
3524         add_wch(&temp);
3525         for (count = 1; count < repeat; ++count) {
3526             add_wch(&temp);
3527         }
3528     }
3529 }
3530
3531 static void
3532 show_upper_widechars(int first, int repeat, int space, attr_t attr, NCURSES_PAIRS_T pair)
3533 {
3534     cchar_t temp;
3535     wchar_t code;
3536     int last = first + 31;
3537
3538     erase();
3539     attron(A_BOLD);
3540     MvPrintw(0, 20, "Display of Character Codes %d to %d", first, last);
3541     attroff(A_BOLD);
3542
3543     for (code = first; (int) code <= last; code++) {
3544         int row = 2 + ((code - first) % 16);
3545         int col = ((code - first) / 16) * COLS / 2;
3546         wchar_t codes[10];
3547         char tmp[80];
3548         int count = repeat;
3549         int y, x;
3550
3551         sprintf(tmp, "%3ld (0x%lx)", (long) code, (long) code);
3552         MvPrintw(row, col, "%*s: ", COLS / 4, tmp);
3553
3554         memset(&codes, 0, sizeof(codes));
3555         codes[0] = code;
3556         setcchar(&temp, codes, attr, pair, 0);
3557
3558         do {
3559             /*
3560              * Give non-spacing characters something to combine with.  If we
3561              * don't, they'll bunch up in a heap on the space after the ":".
3562              * Mark them with reverse-video to make them simpler to find on
3563              * the display.
3564              */
3565             if (wcwidth(code) == 0) {
3566                 addch((chtype) space |
3567                       (A_REVERSE ^ attr) |
3568                       (attr_t) COLOR_PAIR(pair));
3569             }
3570             /*
3571              * This uses echo_wchar(), for comparison with the normal 'f'
3572              * test (and to make a test-case for echo_wchar()).  The screen
3573              * may flicker because the erase() at the top of the function
3574              * is met by the builtin refresh() in echo_wchar().
3575              */
3576             echo_wchar(&temp);
3577             /*
3578              * The repeat-count may make text wrap - avoid that.
3579              */
3580             getyx(stdscr, y, x);
3581             (void) y;
3582             if (x >= col + (COLS / 2) - 2)
3583                 break;
3584         } while (--count > 0);
3585     }
3586 }
3587
3588 static int
3589 show_1_wacs(int n, int repeat, const char *name, const cchar_t *code)
3590 {
3591     const int height = 16;
3592     int row = 2 + (n % height);
3593     int col = (n / height) * COLS / 2;
3594
3595     MvPrintw(row, col, "%*s : ", COLS / 4, name);
3596     while (--repeat >= 0) {
3597         add_wch(code);
3598     }
3599     return n + 1;
3600 }
3601
3602 #define MERGE_ATTR(wch) merge_wide_attr(&temp, wch, attr, pair)
3603
3604 static void
3605 show_wacs_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3606 /* display the wide-ACS character set */
3607 {
3608     cchar_t temp;
3609
3610     int n;
3611
3612 /*#define BOTH2(name) #name, &(name) */
3613 #define BOTH2(name) #name, MERGE_ATTR(name)
3614
3615     erase();
3616     attron(A_BOLD);
3617     MvAddStr(0, 20, "Display of the Wide-ACS Character Set");
3618     attroff(A_BOLD);
3619     refresh();
3620
3621     n = show_1_wacs(0, repeat, BOTH2(WACS_ULCORNER));
3622     n = show_1_wacs(n, repeat, BOTH2(WACS_URCORNER));
3623     n = show_1_wacs(n, repeat, BOTH2(WACS_LLCORNER));
3624     n = show_1_wacs(n, repeat, BOTH2(WACS_LRCORNER));
3625
3626     n = show_1_wacs(n, repeat, BOTH2(WACS_LTEE));
3627     n = show_1_wacs(n, repeat, BOTH2(WACS_RTEE));
3628     n = show_1_wacs(n, repeat, BOTH2(WACS_TTEE));
3629     n = show_1_wacs(n, repeat, BOTH2(WACS_BTEE));
3630
3631     n = show_1_wacs(n, repeat, BOTH2(WACS_HLINE));
3632     n = show_1_wacs(n, repeat, BOTH2(WACS_VLINE));
3633
3634     n = show_1_wacs(n, repeat, BOTH2(WACS_LARROW));
3635     n = show_1_wacs(n, repeat, BOTH2(WACS_RARROW));
3636     n = show_1_wacs(n, repeat, BOTH2(WACS_UARROW));
3637     n = show_1_wacs(n, repeat, BOTH2(WACS_DARROW));
3638
3639     n = show_1_wacs(n, repeat, BOTH2(WACS_BLOCK));
3640     n = show_1_wacs(n, repeat, BOTH2(WACS_BOARD));
3641     n = show_1_wacs(n, repeat, BOTH2(WACS_LANTERN));
3642     n = show_1_wacs(n, repeat, BOTH2(WACS_BULLET));
3643     n = show_1_wacs(n, repeat, BOTH2(WACS_CKBOARD));
3644     n = show_1_wacs(n, repeat, BOTH2(WACS_DEGREE));
3645     n = show_1_wacs(n, repeat, BOTH2(WACS_DIAMOND));
3646     n = show_1_wacs(n, repeat, BOTH2(WACS_PLMINUS));
3647     n = show_1_wacs(n, repeat, BOTH2(WACS_PLUS));
3648
3649 #ifdef CURSES_WACS_ARRAY
3650     n = show_1_wacs(n, repeat, BOTH2(WACS_GEQUAL));
3651     n = show_1_wacs(n, repeat, BOTH2(WACS_NEQUAL));
3652     n = show_1_wacs(n, repeat, BOTH2(WACS_LEQUAL));
3653
3654     n = show_1_wacs(n, repeat, BOTH2(WACS_STERLING));
3655     n = show_1_wacs(n, repeat, BOTH2(WACS_PI));
3656     n = show_1_wacs(n, repeat, BOTH2(WACS_S1));
3657     n = show_1_wacs(n, repeat, BOTH2(WACS_S3));
3658     n = show_1_wacs(n, repeat, BOTH2(WACS_S7));
3659     (void) show_1_wacs(n, repeat, BOTH2(WACS_S9));
3660 #endif
3661 }
3662
3663 #ifdef WACS_D_PLUS
3664 static void
3665 show_wacs_chars_double(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3666 /* display the wide-ACS character set */
3667 {
3668     cchar_t temp;
3669
3670     int n;
3671
3672 /*#define BOTH2(name) #name, &(name) */
3673 #define BOTH2(name) #name, MERGE_ATTR(name)
3674
3675     erase();
3676     attron(A_BOLD);
3677     MvAddStr(0, 20, "Display of the Wide-ACS Character Set");
3678     attroff(A_BOLD);
3679     refresh();
3680
3681     n = show_1_wacs(0, repeat, BOTH2(WACS_D_ULCORNER));
3682     n = show_1_wacs(n, repeat, BOTH2(WACS_D_URCORNER));
3683     n = show_1_wacs(n, repeat, BOTH2(WACS_D_LLCORNER));
3684     n = show_1_wacs(n, repeat, BOTH2(WACS_D_LRCORNER));
3685
3686     n = show_1_wacs(n, repeat, BOTH2(WACS_D_LTEE));
3687     n = show_1_wacs(n, repeat, BOTH2(WACS_D_RTEE));
3688     n = show_1_wacs(n, repeat, BOTH2(WACS_D_TTEE));
3689     n = show_1_wacs(n, repeat, BOTH2(WACS_D_BTEE));
3690
3691     n = show_1_wacs(n, repeat, BOTH2(WACS_D_HLINE));
3692     n = show_1_wacs(n, repeat, BOTH2(WACS_D_VLINE));
3693
3694     n = show_1_wacs(n, repeat, BOTH2(WACS_LARROW));
3695     n = show_1_wacs(n, repeat, BOTH2(WACS_RARROW));
3696     n = show_1_wacs(n, repeat, BOTH2(WACS_UARROW));
3697     n = show_1_wacs(n, repeat, BOTH2(WACS_DARROW));
3698
3699     n = show_1_wacs(n, repeat, BOTH2(WACS_BLOCK));
3700     n = show_1_wacs(n, repeat, BOTH2(WACS_BOARD));
3701     n = show_1_wacs(n, repeat, BOTH2(WACS_LANTERN));
3702     n = show_1_wacs(n, repeat, BOTH2(WACS_BULLET));
3703     n = show_1_wacs(n, repeat, BOTH2(WACS_CKBOARD));
3704     n = show_1_wacs(n, repeat, BOTH2(WACS_DEGREE));
3705     n = show_1_wacs(n, repeat, BOTH2(WACS_DIAMOND));
3706     n = show_1_wacs(n, repeat, BOTH2(WACS_PLMINUS));
3707     n = show_1_wacs(n, repeat, BOTH2(WACS_PLUS));
3708
3709 #ifdef CURSES_WACS_ARRAY
3710     n = show_1_wacs(n, repeat, BOTH2(WACS_GEQUAL));
3711     n = show_1_wacs(n, repeat, BOTH2(WACS_NEQUAL));
3712     n = show_1_wacs(n, repeat, BOTH2(WACS_LEQUAL));
3713
3714     n = show_1_wacs(n, repeat, BOTH2(WACS_STERLING));
3715     n = show_1_wacs(n, repeat, BOTH2(WACS_PI));
3716     n = show_1_wacs(n, repeat, BOTH2(WACS_S1));
3717     n = show_1_wacs(n, repeat, BOTH2(WACS_S3));
3718     n = show_1_wacs(n, repeat, BOTH2(WACS_S7));
3719     (void) show_1_wacs(n, repeat, BOTH2(WACS_S9));
3720 #endif
3721 }
3722 #endif
3723
3724 #ifdef WACS_T_PLUS
3725 static void
3726 show_wacs_chars_thick(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3727 /* display the wide-ACS character set */
3728 {
3729     cchar_t temp;
3730
3731     int n;
3732
3733 /*#define BOTH2(name) #name, &(name) */
3734 #define BOTH2(name) #name, MERGE_ATTR(name)
3735
3736     erase();
3737     attron(A_BOLD);
3738     MvAddStr(0, 20, "Display of the Wide-ACS Character Set");
3739     attroff(A_BOLD);
3740     refresh();
3741
3742     n = show_1_wacs(0, repeat, BOTH2(WACS_T_ULCORNER));
3743     n = show_1_wacs(n, repeat, BOTH2(WACS_T_URCORNER));
3744     n = show_1_wacs(n, repeat, BOTH2(WACS_T_LLCORNER));
3745     n = show_1_wacs(n, repeat, BOTH2(WACS_T_LRCORNER));
3746
3747     n = show_1_wacs(n, repeat, BOTH2(WACS_T_LTEE));
3748     n = show_1_wacs(n, repeat, BOTH2(WACS_T_RTEE));
3749     n = show_1_wacs(n, repeat, BOTH2(WACS_T_TTEE));
3750     n = show_1_wacs(n, repeat, BOTH2(WACS_T_BTEE));
3751
3752     n = show_1_wacs(n, repeat, BOTH2(WACS_T_HLINE));
3753     n = show_1_wacs(n, repeat, BOTH2(WACS_T_VLINE));
3754
3755     n = show_1_wacs(n, repeat, BOTH2(WACS_LARROW));
3756     n = show_1_wacs(n, repeat, BOTH2(WACS_RARROW));
3757     n = show_1_wacs(n, repeat, BOTH2(WACS_UARROW));
3758     n = show_1_wacs(n, repeat, BOTH2(WACS_DARROW));
3759
3760     n = show_1_wacs(n, repeat, BOTH2(WACS_BLOCK));
3761     n = show_1_wacs(n, repeat, BOTH2(WACS_BOARD));
3762     n = show_1_wacs(n, repeat, BOTH2(WACS_LANTERN));
3763     n = show_1_wacs(n, repeat, BOTH2(WACS_BULLET));
3764     n = show_1_wacs(n, repeat, BOTH2(WACS_CKBOARD));
3765     n = show_1_wacs(n, repeat, BOTH2(WACS_DEGREE));
3766     n = show_1_wacs(n, repeat, BOTH2(WACS_DIAMOND));
3767     n = show_1_wacs(n, repeat, BOTH2(WACS_PLMINUS));
3768     n = show_1_wacs(n, repeat, BOTH2(WACS_PLUS));
3769
3770 #ifdef CURSES_WACS_ARRAY
3771     n = show_1_wacs(n, repeat, BOTH2(WACS_GEQUAL));
3772     n = show_1_wacs(n, repeat, BOTH2(WACS_NEQUAL));
3773     n = show_1_wacs(n, repeat, BOTH2(WACS_LEQUAL));
3774
3775     n = show_1_wacs(n, repeat, BOTH2(WACS_STERLING));
3776     n = show_1_wacs(n, repeat, BOTH2(WACS_PI));
3777     n = show_1_wacs(n, repeat, BOTH2(WACS_S1));
3778     n = show_1_wacs(n, repeat, BOTH2(WACS_S3));
3779     n = show_1_wacs(n, repeat, BOTH2(WACS_S7));
3780     (void) show_1_wacs(n, repeat, BOTH2(WACS_S9));
3781 #endif
3782 }
3783 #endif
3784
3785 #undef MERGE_ATTR
3786
3787 #define MERGE_ATTR(n,wch) merge_wide_attr(&temp[n], wch, attr, pair)
3788
3789 static void
3790 show_wbox_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3791 {
3792     cchar_t temp[8];
3793
3794     (void) repeat;
3795     erase();
3796     attron(A_BOLD);
3797     MvAddStr(0, 20, "Display of the Wide-ACS Line-Drawing Set");
3798     attroff(A_BOLD);
3799     refresh();
3800
3801     wborder_set(stdscr,
3802                 MERGE_ATTR(0, WACS_VLINE),
3803                 MERGE_ATTR(1, WACS_VLINE),
3804                 MERGE_ATTR(2, WACS_HLINE),
3805                 MERGE_ATTR(3, WACS_HLINE),
3806                 MERGE_ATTR(4, WACS_ULCORNER),
3807                 MERGE_ATTR(5, WACS_URCORNER),
3808                 MERGE_ATTR(6, WACS_LLCORNER),
3809                 MERGE_ATTR(7, WACS_LRCORNER));
3810     /* *INDENT-OFF* */
3811     (void) mvhline_set(LINES / 2, 0,        MERGE_ATTR(0, WACS_HLINE), COLS);
3812     (void) mvvline_set(0,         COLS / 2, MERGE_ATTR(0, WACS_VLINE), LINES);
3813     (void) mvadd_wch(0,           COLS / 2, MERGE_ATTR(0, WACS_TTEE));
3814     (void) mvadd_wch(LINES / 2,   COLS / 2, MERGE_ATTR(0, WACS_PLUS));
3815     (void) mvadd_wch(LINES - 1,   COLS / 2, MERGE_ATTR(0, WACS_BTEE));
3816     (void) mvadd_wch(LINES / 2,   0,        MERGE_ATTR(0, WACS_LTEE));
3817     (void) mvadd_wch(LINES / 2,   COLS - 1, MERGE_ATTR(0, WACS_RTEE));
3818     /* *INDENT-ON* */
3819
3820 }
3821
3822 #undef MERGE_ATTR
3823
3824 static int
3825 show_2_wacs(int n, const char *name, const char *code, attr_t attr, NCURSES_PAIRS_T pair)
3826 {
3827     const int height = 16;
3828     int row = 2 + (n % height);
3829     int col = (n / height) * COLS / 2;
3830     char temp[80];
3831
3832     MvPrintw(row, col, "%*s : ", COLS / 4, name);
3833     (void) attr_set(attr, pair, 0);
3834     addstr(strncpy(temp, code, 20));
3835     (void) attr_set(A_NORMAL, 0, 0);
3836     return n + 1;
3837 }
3838
3839 #define SHOW_UTF8(n, name, code) show_2_wacs(n, name, code, attr, pair)
3840
3841 static void
3842 show_utf8_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3843 {
3844     int n;
3845
3846     (void) repeat;
3847     erase();
3848     attron(A_BOLD);
3849     MvAddStr(0, 20, "Display of the Wide-ACS Character Set");
3850     attroff(A_BOLD);
3851     refresh();
3852     /* *INDENT-OFF* */
3853     n = SHOW_UTF8(0, "WACS_ULCORNER",   "\342\224\214");
3854     n = SHOW_UTF8(n, "WACS_URCORNER",   "\342\224\220");
3855     n = SHOW_UTF8(n, "WACS_LLCORNER",   "\342\224\224");
3856     n = SHOW_UTF8(n, "WACS_LRCORNER",   "\342\224\230");
3857
3858     n = SHOW_UTF8(n, "WACS_LTEE",       "\342\224\234");
3859     n = SHOW_UTF8(n, "WACS_RTEE",       "\342\224\244");
3860     n = SHOW_UTF8(n, "WACS_TTEE",       "\342\224\254");
3861     n = SHOW_UTF8(n, "WACS_BTEE",       "\342\224\264");
3862
3863     n = SHOW_UTF8(n, "WACS_HLINE",      "\342\224\200");
3864     n = SHOW_UTF8(n, "WACS_VLINE",      "\342\224\202");
3865
3866     n = SHOW_UTF8(n, "WACS_LARROW",     "\342\206\220");
3867     n = SHOW_UTF8(n, "WACS_RARROW",     "\342\206\222");
3868     n = SHOW_UTF8(n, "WACS_UARROW",     "\342\206\221");
3869     n = SHOW_UTF8(n, "WACS_DARROW",     "\342\206\223");
3870
3871     n = SHOW_UTF8(n, "WACS_BLOCK",      "\342\226\256");
3872     n = SHOW_UTF8(n, "WACS_BOARD",      "\342\226\222");
3873     n = SHOW_UTF8(n, "WACS_LANTERN",    "\342\230\203");
3874     n = SHOW_UTF8(n, "WACS_BULLET",     "\302\267");
3875     n = SHOW_UTF8(n, "WACS_CKBOARD",    "\342\226\222");
3876     n = SHOW_UTF8(n, "WACS_DEGREE",     "\302\260");
3877     n = SHOW_UTF8(n, "WACS_DIAMOND",    "\342\227\206");
3878     n = SHOW_UTF8(n, "WACS_PLMINUS",    "\302\261");
3879     n = SHOW_UTF8(n, "WACS_PLUS",       "\342\224\274");
3880     n = SHOW_UTF8(n, "WACS_GEQUAL",     "\342\211\245");
3881     n = SHOW_UTF8(n, "WACS_NEQUAL",     "\342\211\240");
3882     n = SHOW_UTF8(n, "WACS_LEQUAL",     "\342\211\244");
3883
3884     n = SHOW_UTF8(n, "WACS_STERLING",   "\302\243");
3885     n = SHOW_UTF8(n, "WACS_PI",         "\317\200");
3886     n = SHOW_UTF8(n, "WACS_S1",         "\342\216\272");
3887     n = SHOW_UTF8(n, "WACS_S3",         "\342\216\273");
3888     n = SHOW_UTF8(n, "WACS_S7",         "\342\216\274");
3889     (void) SHOW_UTF8(n, "WACS_S9",      "\342\216\275");
3890     /* *INDENT-ON* */
3891
3892 }
3893
3894 /* display the wide-ACS character set */
3895 static void
3896 wide_acs_display(void)
3897 {
3898     int c = 'a';
3899     int digit = 0;
3900     int repeat = 1;
3901     int space = ' ';
3902     int pagesize = 32;
3903     chtype attr = A_NORMAL;
3904     int fg = COLOR_BLACK;
3905     int bg = COLOR_BLACK;
3906     unsigned at_code = 0;
3907     NCURSES_PAIRS_T pair = 0;
3908     void (*last_show_wacs) (int, attr_t, NCURSES_PAIRS_T) = 0;
3909     ATTR_TBL my_list[SIZEOF(attrs_to_test)];
3910     unsigned my_size = init_attr_list(my_list, term_attrs());
3911
3912     do {
3913         switch (c) {
3914         case CTRL('L'):
3915             Repaint();
3916             break;
3917         case 'a':
3918             ToggleAcs(last_show_wacs, show_wacs_chars);
3919             break;
3920 #ifdef WACS_D_PLUS
3921         case 'd':
3922             ToggleAcs(last_show_wacs, show_wacs_chars_double);
3923             break;
3924 #endif
3925 #ifdef WACS_T_PLUS
3926         case 't':
3927             ToggleAcs(last_show_wacs, show_wacs_chars_thick);
3928             break;
3929 #endif
3930         case 'w':
3931             if (pagesize == 32) {
3932                 pagesize = 256;
3933             } else {
3934                 pagesize = 32;
3935             }
3936             break;
3937         case 'x':
3938             ToggleAcs(last_show_wacs, show_wbox_chars);
3939             break;
3940         case 'u':
3941             ToggleAcs(last_show_wacs, show_utf8_chars);
3942             break;
3943         default:
3944             if (c < 256 && isdigit(c)) {
3945                 digit = (c - '0');
3946                 last_show_wacs = 0;
3947             } else if (c == '+') {
3948                 ++digit;
3949                 last_show_wacs = 0;
3950             } else if (c == '-' && digit > 0) {
3951                 --digit;
3952                 last_show_wacs = 0;
3953             } else if (c == '>' && repeat < (COLS / 4)) {
3954                 ++repeat;
3955             } else if (c == '<' && repeat > 1) {
3956                 --repeat;
3957             } else if (c == '_') {
3958                 space = (space == ' ') ? '_' : ' ';
3959                 last_show_wacs = 0;
3960             } else if (cycle_attr(c, &at_code, &attr, my_list, my_size)
3961                        || cycle_colors(c, &fg, &bg, &pair)) {
3962                 if (last_show_wacs != 0)
3963                     break;
3964             } else {
3965                 beep();
3966                 break;
3967             }
3968             break;
3969         }
3970         if (pagesize != 32) {
3971             show_paged_widechars(digit, pagesize, repeat, space, attr, pair);
3972         } else if (last_show_wacs != 0) {
3973             last_show_wacs(repeat, attr, pair);
3974         } else {
3975             show_upper_widechars(digit * 32 + 128, repeat, space, attr, pair);
3976         }
3977
3978         MvPrintw(LINES - 4, 0,
3979                  "Select: a/d/t WACS, w=all x=box, u UTF-8, ^L repaint");
3980         MvPrintw(LINES - 3, 2,
3981                  "0-9,+/- non-ASCII, </> repeat, _ space, ESC=quit");
3982         if (use_colors) {
3983             MvPrintw(LINES - 2, 2,
3984                      "v/V, f/F, b/B cycle through video attributes (%s) and color %d/%d.",
3985                      my_list[at_code].name,
3986                      fg, bg);
3987         } else {
3988             MvPrintw(LINES - 2, 2,
3989                      "v/V cycles through video attributes (%s).",
3990                      my_list[at_code].name);
3991         }
3992         refresh();
3993     } while (!isQuit(c = Getchar()));
3994
3995     Pause();
3996     erase();
3997     endwin();
3998 }
3999
4000 #endif
4001
4002 /*
4003  * Graphic-rendition test (adapted from vttest)
4004  */
4005 static void
4006 test_sgr_attributes(void)
4007 {
4008     int pass;
4009
4010     for (pass = 0; pass < 2; pass++) {
4011         chtype normal = ((pass == 0 ? A_NORMAL : A_REVERSE)) | BLANK;
4012
4013         /* Use non-default colors if possible to exercise bce a little */
4014         if (use_colors) {
4015             init_pair(1, COLOR_WHITE, COLOR_BLUE);
4016             normal |= (chtype) COLOR_PAIR(1);
4017         }
4018         bkgdset(normal);
4019         erase();
4020         MvPrintw(1, 20, "Graphic rendition test pattern:");
4021
4022         MvPrintw(4, 1, "vanilla");
4023
4024 #define set_sgr(mask) bkgdset((normal^(mask)));
4025         set_sgr(A_BOLD);
4026         MvPrintw(4, 40, "bold");
4027
4028         set_sgr(A_UNDERLINE);
4029         MvPrintw(6, 6, "underline");
4030
4031         set_sgr(A_BOLD | A_UNDERLINE);
4032         MvPrintw(6, 45, "bold underline");
4033
4034         set_sgr(A_BLINK);
4035         MvPrintw(8, 1, "blink");
4036
4037         set_sgr(A_BLINK | A_BOLD);
4038         MvPrintw(8, 40, "bold blink");
4039
4040         set_sgr(A_UNDERLINE | A_BLINK);
4041         MvPrintw(10, 6, "underline blink");
4042
4043         set_sgr(A_BOLD | A_UNDERLINE | A_BLINK);
4044         MvPrintw(10, 45, "bold underline blink");
4045
4046         set_sgr(A_REVERSE);
4047         MvPrintw(12, 1, "negative");
4048
4049         set_sgr(A_BOLD | A_REVERSE);
4050         MvPrintw(12, 40, "bold negative");
4051
4052         set_sgr(A_UNDERLINE | A_REVERSE);
4053         MvPrintw(14, 6, "underline negative");
4054
4055         set_sgr(A_BOLD | A_UNDERLINE | A_REVERSE);
4056         MvPrintw(14, 45, "bold underline negative");
4057
4058         set_sgr(A_BLINK | A_REVERSE);
4059         MvPrintw(16, 1, "blink negative");
4060
4061         set_sgr(A_BOLD | A_BLINK | A_REVERSE);
4062         MvPrintw(16, 40, "bold blink negative");
4063
4064         set_sgr(A_UNDERLINE | A_BLINK | A_REVERSE);
4065         MvPrintw(18, 6, "underline blink negative");
4066
4067         set_sgr(A_BOLD | A_UNDERLINE | A_BLINK | A_REVERSE);
4068         MvPrintw(18, 45, "bold underline blink negative");
4069
4070         bkgdset(normal);
4071         MvPrintw(LINES - 2, 1, "%s background. ", pass == 0 ? "Dark" :
4072                  "Light");
4073         clrtoeol();
4074         Pause();
4075     }
4076
4077     bkgdset(A_NORMAL | BLANK);
4078     erase();
4079     endwin();
4080 }
4081
4082 /****************************************************************************
4083  *
4084  * Windows and scrolling tester.
4085  *
4086  ****************************************************************************/
4087
4088 #define BOTLINES        4       /* number of line stolen from screen bottom */
4089
4090 typedef struct {
4091     int y, x;
4092 } pair;
4093
4094 #define FRAME struct frame
4095 FRAME
4096 {
4097     FRAME *next, *last;
4098     bool do_scroll;
4099     bool do_keypad;
4100     WINDOW *wind;
4101 };
4102
4103 #if defined(NCURSES_VERSION)
4104 #if (NCURSES_VERSION_PATCH < 20070331) && NCURSES_EXT_FUNCS
4105 #define is_keypad(win)   (win)->_use_keypad
4106 #define is_scrollok(win) (win)->_scroll
4107 #elif !defined(is_keypad)
4108 #define is_keypad(win)   FALSE
4109 #define is_scrollok(win) FALSE
4110 #endif
4111 #else
4112 #define is_keypad(win)   FALSE
4113 #define is_scrollok(win) FALSE
4114 #endif
4115
4116 static WINDOW *
4117 frame_win(FRAME * curp)
4118 {
4119     return (curp != 0) ? curp->wind : stdscr;
4120 }
4121
4122 /* We need to know if these flags are actually set, so don't look in FRAME.
4123  * These names are known to work with SVr4 curses as well as ncurses.  The
4124  * _use_keypad name does not work with Solaris 8.
4125  */
4126 static bool
4127 HaveKeypad(FRAME * curp)
4128 {
4129     WINDOW *win = frame_win(curp);
4130     (void) win;
4131     return is_keypad(win);
4132 }
4133
4134 static bool
4135 HaveScroll(FRAME * curp)
4136 {
4137     WINDOW *win = frame_win(curp);
4138     (void) win;
4139     return is_scrollok(win);
4140 }
4141
4142 static void
4143 newwin_legend(FRAME * curp)
4144 {
4145     static const struct {
4146         const char *msg;
4147         int code;
4148     } legend[] = {
4149         {
4150             "^C = create window", 0
4151         },
4152         {
4153             "^N = next window", 0
4154         },
4155         {
4156             "^P = previous window", 0
4157         },
4158         {
4159             "^F = scroll forward", 0
4160         },
4161         {
4162             "^B = scroll backward", 0
4163         },
4164         {
4165             "^K = keypad(%s)", 1
4166         },
4167         {
4168             "^S = scrollok(%s)", 2
4169         },
4170         {
4171             "^W = save window to file", 0
4172         },
4173         {
4174             "^R = restore window", 0
4175         },
4176 #if HAVE_WRESIZE
4177         {
4178             "^X = resize", 0
4179         },
4180 #endif
4181         {
4182             "^Q%s = exit", 3
4183         }
4184     };
4185     size_t n;
4186     int x;
4187     bool do_keypad = HaveKeypad(curp);
4188     bool do_scroll = HaveScroll(curp);
4189     char buf[BUFSIZ];
4190
4191     move(LINES - 4, 0);
4192     for (n = 0; n < SIZEOF(legend); n++) {
4193         switch (legend[n].code) {
4194         default:
4195             strcpy(buf, legend[n].msg);
4196             break;
4197         case 1:
4198             sprintf(buf, legend[n].msg, do_keypad ? "yes" : "no");
4199             break;
4200         case 2:
4201             sprintf(buf, legend[n].msg, do_scroll ? "yes" : "no");
4202             break;
4203         case 3:
4204             sprintf(buf, legend[n].msg, do_keypad ? "/ESC" : "");
4205             break;
4206         }
4207         x = getcurx(stdscr);
4208         addstr((COLS < (x + 3 + (int) strlen(buf))) ? "\n" : (n ? ", " : ""));
4209         addstr(buf);
4210     }
4211     clrtoeol();
4212 }
4213
4214 static void
4215 transient(FRAME * curp, NCURSES_CONST char *msg)
4216 {
4217     newwin_legend(curp);
4218     if (msg) {
4219         MvAddStr(LINES - 1, 0, msg);
4220         refresh();
4221         napms(1000);
4222     }
4223
4224     move(LINES - 1, 0);
4225     printw("%s characters are echoed, window should %sscroll.",
4226            HaveKeypad(curp) ? "Non-arrow" : "All other",
4227            HaveScroll(curp) ? "" : "not ");
4228     clrtoeol();
4229 }
4230
4231 static void
4232 newwin_report(FRAME * curp)
4233 /* report on the cursor's current position, then restore it */
4234 {
4235     WINDOW *win = frame_win(curp);
4236     int y, x;
4237
4238     if (win != stdscr)
4239         transient(curp, (char *) 0);
4240     getyx(win, y, x);
4241     move(LINES - 1, COLS - 17);
4242     printw("Y = %2d X = %2d", y, x);
4243     if (win != stdscr)
4244         refresh();
4245     else
4246         wmove(win, y, x);
4247 }
4248
4249 static pair *
4250 selectcell(int uli, int ulj, int lri, int lrj)
4251 /* arrows keys move cursor, return location at current on non-arrow key */
4252 {
4253     static pair res;            /* result cell */
4254     int si = lri - uli + 1;     /* depth of the select area */
4255     int sj = lrj - ulj + 1;     /* width of the select area */
4256     int i = 0, j = 0;           /* offsets into the select area */
4257
4258     res.y = uli;
4259     res.x = ulj;
4260     for (;;) {
4261         move(uli + i, ulj + j);
4262         newwin_report((FRAME *) 0);
4263
4264         switch (Getchar()) {
4265         case KEY_UP:
4266             i += si - 1;
4267             break;
4268         case KEY_DOWN:
4269             i++;
4270             break;
4271         case KEY_LEFT:
4272             j += sj - 1;
4273             break;
4274         case KEY_RIGHT:
4275             j++;
4276             break;
4277         case case_QUIT:
4278             return ((pair *) 0);
4279 #ifdef NCURSES_MOUSE_VERSION
4280         case KEY_MOUSE:
4281             {
4282                 MEVENT event;
4283
4284                 getmouse(&event);
4285                 if (event.y > uli && event.x > ulj) {
4286                     i = event.y - uli;
4287                     j = event.x - ulj;
4288                 } else {
4289                     beep();
4290                     break;
4291                 }
4292             }
4293             /* FALLTHRU */
4294 #endif
4295         default:
4296             res.y = uli + i;
4297             res.x = ulj + j;
4298             return (&res);
4299         }
4300         i %= si;
4301         j %= sj;
4302     }
4303 }
4304
4305 static void
4306 outerbox(pair ul, pair lr, bool onoff)
4307 /* draw or erase a box *outside* the given pair of corners */
4308 {
4309     MvAddCh(ul.y - 1, lr.x - 1, onoff ? ACS_ULCORNER : ' ');
4310     MvAddCh(ul.y - 1, lr.x + 1, onoff ? ACS_URCORNER : ' ');
4311     MvAddCh(lr.y + 1, lr.x + 1, onoff ? ACS_LRCORNER : ' ');
4312     MvAddCh(lr.y + 1, ul.x - 1, onoff ? ACS_LLCORNER : ' ');
4313     move(ul.y - 1, ul.x);
4314     hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
4315     move(ul.y, ul.x - 1);
4316     vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
4317     move(lr.y + 1, ul.x);
4318     hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
4319     move(ul.y, lr.x + 1);
4320     vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
4321 }
4322
4323 static WINDOW *
4324 getwindow(void)
4325 /* Ask user for a window definition */
4326 {
4327     WINDOW *rwindow;
4328     pair ul, lr, *tmp;
4329
4330     move(0, 0);
4331     clrtoeol();
4332     addstr("Use arrows to move cursor, anything else to mark corner 1");
4333     refresh();
4334     if ((tmp = selectcell(2, 1, LINES - BOTLINES - 2, COLS - 2)) == (pair *) 0)
4335         return ((WINDOW *) 0);
4336     memcpy(&ul, tmp, sizeof(pair));
4337     MvAddCh(ul.y - 1, ul.x - 1, ACS_ULCORNER);
4338     move(0, 0);
4339     clrtoeol();
4340     addstr("Use arrows to move cursor, anything else to mark corner 2");
4341     refresh();
4342     if ((tmp = selectcell(ul.y, ul.x, LINES - BOTLINES - 2, COLS - 2)) ==
4343         (pair *) 0)
4344         return ((WINDOW *) 0);
4345     memcpy(&lr, tmp, sizeof(pair));
4346
4347     rwindow = subwin(stdscr, lr.y - ul.y + 1, lr.x - ul.x + 1, ul.y, ul.x);
4348
4349     outerbox(ul, lr, TRUE);
4350     refresh();
4351
4352     if (rwindow != 0)
4353         wrefresh(rwindow);
4354
4355     move(0, 0);
4356     clrtoeol();
4357     return (rwindow);
4358 }
4359
4360 static void
4361 newwin_move(FRAME * curp, int dy, int dx)
4362 {
4363     WINDOW *win = frame_win(curp);
4364     int cur_y, cur_x;
4365     int max_y, max_x;
4366
4367     getyx(win, cur_y, cur_x);
4368     getmaxyx(win, max_y, max_x);
4369     if ((cur_x += dx) < 0)
4370         cur_x = 0;
4371     else if (cur_x >= max_x)
4372         cur_x = max_x - 1;
4373     if ((cur_y += dy) < 0)
4374         cur_y = 0;
4375     else if (cur_y >= max_y)
4376         cur_y = max_y - 1;
4377     wmove(win, cur_y, cur_x);
4378 }
4379
4380 static FRAME *
4381 delete_framed(FRAME * fp, bool showit)
4382 {
4383     FRAME *np = 0;
4384
4385     if (fp != 0) {
4386         fp->last->next = fp->next;
4387         fp->next->last = fp->last;
4388
4389         if (showit) {
4390             werase(fp->wind);
4391             wrefresh(fp->wind);
4392         }
4393         delwin(fp->wind);
4394
4395         np = (fp == fp->next) ? 0 : fp->next;
4396         free(fp);
4397     }
4398     return np;
4399 }
4400
4401 static void
4402 acs_and_scroll(void)
4403 /* Demonstrate windows */
4404 {
4405     int c;
4406     FRAME *current = (FRAME *) 0, *neww;
4407     WINDOW *usescr;
4408 #if HAVE_PUTWIN && HAVE_GETWIN
4409     FILE *fp;
4410 #endif
4411
4412 #define DUMPFILE        "screendump"
4413
4414 #ifdef NCURSES_MOUSE_VERSION
4415     mousemask(BUTTON1_CLICKED, (mmask_t *) 0);
4416 #endif
4417     c = CTRL('C');
4418     raw();
4419     do {
4420         transient((FRAME *) 0, (char *) 0);
4421         switch (c) {
4422         case CTRL('C'):
4423             if ((neww = typeCalloc(FRAME, (size_t) 1)) == 0) {
4424                 failed("acs_and_scroll");
4425                 goto breakout;
4426             }
4427             if ((neww->wind = getwindow()) == (WINDOW *) 0) {
4428                 failed("acs_and_scroll");
4429                 free(neww);
4430                 goto breakout;
4431             }
4432
4433             if (current == 0) { /* First element,  */
4434                 neww->next = neww;      /*   so point it at itself */
4435                 neww->last = neww;
4436             } else {
4437                 neww->next = current->next;
4438                 neww->last = current;
4439                 neww->last->next = neww;
4440                 neww->next->last = neww;
4441             }
4442             current = neww;
4443             /* SVr4 curses sets the keypad on all newly-created windows to
4444              * false.  Someone reported that PDCurses makes new windows inherit
4445              * this flag.  Remove the following 'keypad()' call to test this
4446              */
4447             keypad(current->wind, TRUE);
4448             current->do_keypad = HaveKeypad(current);
4449             current->do_scroll = HaveScroll(current);
4450             break;
4451
4452         case CTRL('N'): /* go to next window */
4453             if (current)
4454                 current = current->next;
4455             break;
4456
4457         case CTRL('P'): /* go to previous window */
4458             if (current)
4459                 current = current->last;
4460             break;
4461
4462         case CTRL('F'): /* scroll current window forward */
4463             if (current)
4464                 wscrl(frame_win(current), 1);
4465             break;
4466
4467         case CTRL('B'): /* scroll current window backwards */
4468             if (current)
4469                 wscrl(frame_win(current), -1);
4470             break;
4471
4472         case CTRL('K'): /* toggle keypad mode for current */
4473             if (current) {
4474                 current->do_keypad = !current->do_keypad;
4475                 keypad(current->wind, current->do_keypad);
4476             }
4477             break;
4478
4479         case CTRL('S'):
4480             if (current) {
4481                 current->do_scroll = !current->do_scroll;
4482                 scrollok(current->wind, current->do_scroll);
4483             }
4484             break;
4485
4486 #if HAVE_PUTWIN && HAVE_GETWIN
4487         case CTRL('W'): /* save and delete window */
4488             if ((current != 0) && (current == current->next)) {
4489                 transient(current, "Will not save/delete ONLY window");
4490                 break;
4491             } else if ((fp = fopen(DUMPFILE, "w")) == (FILE *) 0) {
4492                 transient(current, "Can't open screen dump file");
4493             } else {
4494                 (void) putwin(frame_win(current), fp);
4495                 (void) fclose(fp);
4496
4497                 current = delete_framed(current, TRUE);
4498             }
4499             break;
4500
4501         case CTRL('R'): /* restore window */
4502             if ((fp = fopen(DUMPFILE, "r")) == (FILE *) 0) {
4503                 transient(current, "Can't open screen dump file");
4504             } else {
4505                 if ((neww = typeCalloc(FRAME, (size_t) 1)) != 0) {
4506
4507                     neww->next = current ? current->next : 0;
4508                     neww->last = current;
4509                     if (neww->last != 0)
4510                         neww->last->next = neww;
4511                     if (neww->next != 0)
4512                         neww->next->last = neww;
4513
4514                     neww->wind = getwin(fp);
4515
4516                     wrefresh(neww->wind);
4517                 } else {
4518                     failed("acs_and_scroll");
4519                 }
4520                 (void) fclose(fp);
4521             }
4522             break;
4523 #endif
4524
4525 #if HAVE_WRESIZE
4526         case CTRL('X'): /* resize window */
4527             if (current) {
4528                 pair *tmp, ul, lr;
4529                 int i, mx, my;
4530
4531                 move(0, 0);
4532                 clrtoeol();
4533                 addstr("Use arrows to move cursor, anything else to mark new corner");
4534                 refresh();
4535
4536                 getbegyx(current->wind, ul.y, ul.x);
4537
4538                 tmp = selectcell(ul.y, ul.x, LINES - BOTLINES - 2, COLS - 2);
4539                 if (tmp == (pair *) 0) {
4540                     beep();
4541                     break;
4542                 }
4543
4544                 getmaxyx(current->wind, lr.y, lr.x);
4545                 lr.y += (ul.y - 1);
4546                 lr.x += (ul.x - 1);
4547                 outerbox(ul, lr, FALSE);
4548                 wnoutrefresh(stdscr);
4549
4550                 /* strictly cosmetic hack for the test */
4551                 getmaxyx(current->wind, my, mx);
4552                 if (my > tmp->y - ul.y) {
4553                     getyx(current->wind, lr.y, lr.x);
4554                     wmove(current->wind, tmp->y - ul.y + 1, 0);
4555                     wclrtobot(current->wind);
4556                     wmove(current->wind, lr.y, lr.x);
4557                 }
4558                 if (mx > tmp->x - ul.x)
4559                     for (i = 0; i < my; i++) {
4560                         wmove(current->wind, i, tmp->x - ul.x + 1);
4561                         wclrtoeol(current->wind);
4562                     }
4563                 wnoutrefresh(current->wind);
4564
4565                 memcpy(&lr, tmp, sizeof(pair));
4566                 (void) wresize(current->wind, lr.y - ul.y + 0, lr.x - ul.x + 0);
4567
4568                 getbegyx(current->wind, ul.y, ul.x);
4569                 getmaxyx(current->wind, lr.y, lr.x);
4570                 lr.y += (ul.y - 1);
4571                 lr.x += (ul.x - 1);
4572                 outerbox(ul, lr, TRUE);
4573                 wnoutrefresh(stdscr);
4574
4575                 wnoutrefresh(current->wind);
4576                 move(0, 0);
4577                 clrtoeol();
4578                 doupdate();
4579             }
4580             break;
4581 #endif /* HAVE_WRESIZE */
4582
4583         case KEY_F(10): /* undocumented --- use this to test area clears */
4584             selectcell(0, 0, LINES - 1, COLS - 1);
4585             clrtobot();
4586             refresh();
4587             break;
4588
4589         case KEY_UP:
4590             newwin_move(current, -1, 0);
4591             break;
4592         case KEY_DOWN:
4593             newwin_move(current, 1, 0);
4594             break;
4595         case KEY_LEFT:
4596             newwin_move(current, 0, -1);
4597             break;
4598         case KEY_RIGHT:
4599             newwin_move(current, 0, 1);
4600             break;
4601
4602         case KEY_BACKSPACE:
4603             /* FALLTHROUGH */
4604         case KEY_DC:
4605             {
4606                 int y, x;
4607                 getyx(frame_win(current), y, x);
4608                 if (--x < 0) {
4609                     if (--y < 0)
4610                         break;
4611                     x = getmaxx(frame_win(current)) - 1;
4612                 }
4613                 (void) mvwdelch(frame_win(current), y, x);
4614             }
4615             break;
4616
4617         case '\r':
4618             c = '\n';
4619             /* FALLTHROUGH */
4620
4621         default:
4622             if (current)
4623                 waddch(current->wind, (chtype) c);
4624             else
4625                 beep();
4626             break;
4627         }
4628         newwin_report(current);
4629         usescr = frame_win(current);
4630         wrefresh(usescr);
4631     } while
4632         (!isQuit(c = wGetchar(usescr))
4633          && (c != ERR));
4634
4635   breakout:
4636     while (current != 0)
4637         current = delete_framed(current, FALSE);
4638
4639     scrollok(stdscr, TRUE);     /* reset to driver's default */
4640 #ifdef NCURSES_MOUSE_VERSION
4641     mousemask(0, (mmask_t *) 0);
4642 #endif
4643     noraw();
4644     erase();
4645     endwin();
4646 }
4647
4648 /****************************************************************************
4649  *
4650  * Panels tester
4651  *
4652  ****************************************************************************/
4653
4654 #if USE_LIBPANEL
4655 static int nap_msec = 1;
4656
4657 static NCURSES_CONST char *mod[] =
4658 {
4659     "test ",
4660     "TEST ",
4661     "(**) ",
4662     "*()* ",
4663     "<--> ",
4664     "LAST "
4665 };
4666
4667 /*+-------------------------------------------------------------------------
4668         wait_a_while(msec)
4669 --------------------------------------------------------------------------*/
4670 static void
4671 wait_a_while(int msec GCC_UNUSED)
4672 {
4673 #if HAVE_NAPMS
4674     if (nap_msec == 1)
4675         wGetchar(stdscr);
4676     else
4677         napms(nap_msec);
4678 #else
4679     if (nap_msec == 1)
4680         wGetchar(stdscr);
4681     else if (msec > 1000)
4682         sleep((unsigned) msec / 1000);
4683     else
4684         sleep(1);
4685 #endif
4686 }                               /* end of wait_a_while */
4687
4688 /*+-------------------------------------------------------------------------
4689         saywhat(text)
4690 --------------------------------------------------------------------------*/
4691 static void
4692 saywhat(NCURSES_CONST char *text)
4693 {
4694     wmove(stdscr, LINES - 1, 0);
4695     wclrtoeol(stdscr);
4696     if (text != 0 && *text != '\0') {
4697         waddstr(stdscr, text);
4698         waddstr(stdscr, "; ");
4699     }
4700     waddstr(stdscr, "press any key to continue");
4701 }                               /* end of saywhat */
4702
4703 /*+-------------------------------------------------------------------------
4704         mkpanel(rows,cols,tly,tlx) - alloc a win and panel and associate them
4705 --------------------------------------------------------------------------*/
4706 static PANEL *
4707 mkpanel(NCURSES_COLOR_T color, int rows, int cols, int tly, int tlx)
4708 {
4709     WINDOW *win;
4710     PANEL *pan = 0;
4711
4712     if ((win = newwin(rows, cols, tly, tlx)) != 0) {
4713         if ((pan = new_panel(win)) == 0) {
4714             delwin(win);
4715         } else if (use_colors) {
4716             NCURSES_COLOR_T fg = (NCURSES_COLOR_T) ((color == COLOR_BLUE)
4717                                                     ? COLOR_WHITE
4718                                                     : COLOR_BLACK);
4719             NCURSES_COLOR_T bg = color;
4720
4721             init_pair(color, fg, bg);
4722             wbkgdset(win, (attr_t) (COLOR_PAIR(color) | ' '));
4723         } else {
4724             wbkgdset(win, A_BOLD | ' ');
4725         }
4726     }
4727     return pan;
4728 }                               /* end of mkpanel */
4729
4730 /*+-------------------------------------------------------------------------
4731         rmpanel(pan)
4732 --------------------------------------------------------------------------*/
4733 static void
4734 rmpanel(PANEL * pan)
4735 {
4736     WINDOW *win = panel_window(pan);
4737     del_panel(pan);
4738     delwin(win);
4739 }                               /* end of rmpanel */
4740
4741 /*+-------------------------------------------------------------------------
4742         pflush()
4743 --------------------------------------------------------------------------*/
4744 static void
4745 pflush(void)
4746 {
4747     update_panels();
4748     doupdate();
4749 }                               /* end of pflush */
4750
4751 /*+-------------------------------------------------------------------------
4752         fill_panel(win)
4753 --------------------------------------------------------------------------*/
4754 static void
4755 init_panel(void)
4756 {
4757     register int y, x;
4758
4759     for (y = 0; y < LINES - 1; y++) {
4760         for (x = 0; x < COLS; x++)
4761             wprintw(stdscr, "%d", (y + x) % 10);
4762     }
4763 }
4764
4765 static void
4766 fill_panel(PANEL * pan)
4767 {
4768     WINDOW *win = panel_window(pan);
4769     const char *userptr = (const char *) panel_userptr(pan);
4770     int num = (userptr && *userptr) ? userptr[1] : '?';
4771     int y, x;
4772
4773     wmove(win, 1, 1);
4774     wprintw(win, "-pan%c-", num);
4775     wclrtoeol(win);
4776     box(win, 0, 0);
4777     for (y = 2; y < getmaxy(win) - 1; y++) {
4778         for (x = 1; x < getmaxx(win) - 1; x++) {
4779             wmove(win, y, x);
4780             waddch(win, UChar(num));
4781         }
4782     }
4783 }
4784
4785 #if USE_WIDEC_SUPPORT
4786 static void
4787 init_wide_panel(void)
4788 {
4789     int digit;
4790     cchar_t temp[10];
4791
4792     for (digit = 0; digit < 10; ++digit)
4793         make_fullwidth_digit(&temp[digit], digit);
4794
4795     do {
4796         int y, x;
4797         getyx(stdscr, y, x);
4798         digit = (y + x / 2) % 10;
4799     } while (add_wch(&temp[digit]) != ERR);
4800 }
4801
4802 static void
4803 fill_wide_panel(PANEL * pan)
4804 {
4805     WINDOW *win = panel_window(pan);
4806     const char *userptr = (const char *) panel_userptr(pan);
4807     int num = (userptr && *userptr) ? userptr[1] : '?';
4808     int y, x;
4809
4810     wmove(win, 1, 1);
4811     wprintw(win, "-pan%c-", num);
4812     wclrtoeol(win);
4813     box(win, 0, 0);
4814     for (y = 2; y < getmaxy(win) - 1; y++) {
4815         for (x = 1; x < getmaxx(win) - 1; x++) {
4816             wmove(win, y, x);
4817             waddch(win, UChar(num));
4818         }
4819     }
4820 }
4821 #endif
4822
4823 #define MAX_PANELS 5
4824
4825 static void
4826 canned_panel(PANEL * px[MAX_PANELS + 1], NCURSES_CONST char *cmd)
4827 {
4828     int which = cmd[1] - '0';
4829
4830     saywhat(cmd);
4831     switch (*cmd) {
4832     case 'h':
4833         hide_panel(px[which]);
4834         break;
4835     case 's':
4836         show_panel(px[which]);
4837         break;
4838     case 't':
4839         top_panel(px[which]);
4840         break;
4841     case 'b':
4842         bottom_panel(px[which]);
4843         break;
4844     case 'd':
4845         rmpanel(px[which]);
4846         break;
4847     }
4848     pflush();
4849     wait_a_while(nap_msec);
4850 }
4851
4852 static void
4853 demo_panels(void (*InitPanel) (void), void (*FillPanel) (PANEL *))
4854 {
4855     int count;
4856     int itmp;
4857     PANEL *px[MAX_PANELS + 1];
4858
4859     scrollok(stdscr, FALSE);    /* we don't want stdscr to scroll! */
4860     refresh();
4861
4862     InitPanel();
4863     for (count = 0; count < 5; count++) {
4864         px[1] = mkpanel(COLOR_RED,
4865                         LINES / 2 - 2,
4866                         COLS / 8 + 1,
4867                         0,
4868                         0);
4869         set_panel_userptr(px[1], (NCURSES_CONST void *) "p1");
4870
4871         px[2] = mkpanel(COLOR_GREEN,
4872                         LINES / 2 + 1,
4873                         COLS / 7,
4874                         LINES / 4,
4875                         COLS / 10);
4876         set_panel_userptr(px[2], (NCURSES_CONST void *) "p2");
4877
4878         px[3] = mkpanel(COLOR_YELLOW,
4879                         LINES / 4,
4880                         COLS / 10,
4881                         LINES / 2,
4882                         COLS / 9);
4883         set_panel_userptr(px[3], (NCURSES_CONST void *) "p3");
4884
4885         px[4] = mkpanel(COLOR_BLUE,
4886                         LINES / 2 - 2,
4887                         COLS / 8,
4888                         LINES / 2 - 2,
4889                         COLS / 3);
4890         set_panel_userptr(px[4], (NCURSES_CONST void *) "p4");
4891
4892         px[5] = mkpanel(COLOR_MAGENTA,
4893                         LINES / 2 - 2,
4894                         COLS / 8,
4895                         LINES / 2,
4896                         COLS / 2 - 2);
4897         set_panel_userptr(px[5], (NCURSES_CONST void *) "p5");
4898
4899         FillPanel(px[1]);
4900         FillPanel(px[2]);
4901         FillPanel(px[3]);
4902         FillPanel(px[4]);
4903         FillPanel(px[5]);
4904
4905         hide_panel(px[4]);
4906         hide_panel(px[5]);
4907         pflush();
4908         saywhat("");
4909         wait_a_while(nap_msec);
4910
4911         saywhat("h3 s1 s2 s4 s5");
4912         move_panel(px[1], 0, 0);
4913         hide_panel(px[3]);
4914         show_panel(px[1]);
4915         show_panel(px[2]);
4916         show_panel(px[4]);
4917         show_panel(px[5]);
4918         pflush();
4919         wait_a_while(nap_msec);
4920
4921         canned_panel(px, "s1");
4922         canned_panel(px, "s2");
4923
4924         saywhat("m2");
4925         move_panel(px[2], LINES / 3 + 1, COLS / 8);
4926         pflush();
4927         wait_a_while(nap_msec);
4928
4929         canned_panel(px, "s3");
4930
4931         saywhat("m3");
4932         move_panel(px[3], LINES / 4 + 1, COLS / 15);
4933         pflush();
4934         wait_a_while(nap_msec);
4935
4936         canned_panel(px, "b3");
4937         canned_panel(px, "s4");
4938         canned_panel(px, "s5");
4939         canned_panel(px, "t3");
4940         canned_panel(px, "t1");
4941         canned_panel(px, "t2");
4942         canned_panel(px, "t3");
4943         canned_panel(px, "t4");
4944
4945         for (itmp = 0; itmp < 6; itmp++) {
4946             WINDOW *w4 = panel_window(px[4]);
4947             WINDOW *w5 = panel_window(px[5]);
4948
4949             saywhat("m4");
4950             wmove(w4, LINES / 8, 1);
4951             waddstr(w4, mod[itmp]);
4952             move_panel(px[4], LINES / 6, itmp * (COLS / 8));
4953             wmove(w5, LINES / 6, 1);
4954             waddstr(w5, mod[itmp]);
4955             pflush();
4956             wait_a_while(nap_msec);
4957
4958             saywhat("m5");
4959             wmove(w4, LINES / 6, 1);
4960             waddstr(w4, mod[itmp]);
4961             move_panel(px[5], LINES / 3 - 1, (itmp * 10) + 6);
4962             wmove(w5, LINES / 8, 1);
4963             waddstr(w5, mod[itmp]);
4964             pflush();
4965             wait_a_while(nap_msec);
4966         }
4967
4968         saywhat("m4");
4969         move_panel(px[4], LINES / 6, itmp * (COLS / 8));
4970         pflush();
4971         wait_a_while(nap_msec);
4972
4973         canned_panel(px, "t5");
4974         canned_panel(px, "t2");
4975         canned_panel(px, "t1");
4976         canned_panel(px, "d2");
4977         canned_panel(px, "h3");
4978         canned_panel(px, "d1");
4979         canned_panel(px, "d4");
4980         canned_panel(px, "d5");
4981         canned_panel(px, "d3");
4982
4983         wait_a_while(nap_msec);
4984         if (nap_msec == 1)
4985             break;
4986         nap_msec = 100L;
4987     }
4988
4989     erase();
4990     endwin();
4991 }
4992 #endif /* USE_LIBPANEL */
4993
4994 /****************************************************************************
4995  *
4996  * Pad tester
4997  *
4998  ****************************************************************************/
4999
5000 #define GRIDSIZE        3
5001
5002 static bool pending_pan = FALSE;
5003 static bool show_panner_legend = TRUE;
5004
5005 static int
5006 panner_legend(int line)
5007 {
5008     static const char *const legend[] =
5009     {
5010         "Use arrow keys (or U,D,L,R) to pan, ESC to quit, ! to shell-out.",
5011         "Use +,- (or j,k) to grow/shrink the panner vertically.",
5012         "Use <,> (or h,l) to grow/shrink the panner horizontally.",
5013         "Number repeats.  Toggle legend:? filler:a timer:t scrollmark:s."
5014     };
5015     int n = ((int) SIZEOF(legend) - (LINES - line));
5016     if (n >= 0) {
5017         if (move(line, 0) != ERR) {
5018             if (show_panner_legend)
5019                 printw("%s", legend[n]);
5020             clrtoeol();
5021             return show_panner_legend;
5022         }
5023     }
5024     return FALSE;
5025 }
5026
5027 static void
5028 panner_h_cleanup(int from_y, int from_x, int to_x)
5029 {
5030     if (!panner_legend(from_y))
5031         do_h_line(from_y, from_x, ' ', to_x);
5032 }
5033
5034 static void
5035 panner_v_cleanup(int from_y, int from_x, int to_y)
5036 {
5037     if (!panner_legend(from_y))
5038         do_v_line(from_y, from_x, ' ', to_y);
5039 }
5040
5041 static void
5042 fill_pad(WINDOW *panpad, bool pan_lines)
5043 {
5044     int y, x;
5045     unsigned gridcount = 0;
5046
5047     wmove(panpad, 0, 0);
5048     for (y = 0; y < getmaxy(panpad); y++) {
5049         for (x = 0; x < getmaxx(panpad); x++) {
5050             if (y % GRIDSIZE == 0 && x % GRIDSIZE == 0) {
5051                 if (y == 0 && x == 0)
5052                     waddch(panpad, pan_lines ? ACS_ULCORNER : '+');
5053                 else if (y == 0)
5054                     waddch(panpad, pan_lines ? ACS_TTEE : '+');
5055                 else if (y == 0 || x == 0)
5056                     waddch(panpad, pan_lines ? ACS_LTEE : '+');
5057                 else
5058                     waddch(panpad, (chtype) ((pan_lines ? 'a' : 'A') +
5059                                              (int) (gridcount++ % 26)));
5060             } else if (y % GRIDSIZE == 0)
5061                 waddch(panpad, pan_lines ? ACS_HLINE : '-');
5062             else if (x % GRIDSIZE == 0)
5063                 waddch(panpad, pan_lines ? ACS_VLINE : '|');
5064             else
5065                 waddch(panpad, ' ');
5066         }
5067     }
5068 }
5069
5070 static void
5071 panner(WINDOW *pad,
5072        int top_x, int top_y, int porty, int portx,
5073        int (*pgetc) (WINDOW *))
5074 {
5075 #if HAVE_GETTIMEOFDAY
5076     struct timeval before, after;
5077     bool timing = TRUE;
5078 #endif
5079     bool pan_lines = FALSE;
5080     bool scrollers = TRUE;
5081     int basex = 0;
5082     int basey = 0;
5083     int pxmax, pymax, lowend, highend, c;
5084
5085     getmaxyx(pad, pymax, pxmax);
5086     scrollok(stdscr, FALSE);    /* we don't want stdscr to scroll! */
5087
5088     c = KEY_REFRESH;
5089     do {
5090 #ifdef NCURSES_VERSION
5091         /*
5092          * During shell-out, the user may have resized the window.  Adjust
5093          * the port size of the pad to accommodate this.  Ncurses automatically
5094          * resizes all of the normal windows to fit on the new screen.
5095          */
5096         if (top_x > COLS)
5097             top_x = COLS;
5098         if (portx > COLS)
5099             portx = COLS;
5100         if (top_y > LINES)
5101             top_y = LINES;
5102         if (porty > LINES)
5103             porty = LINES;
5104 #endif
5105         switch (c) {
5106         case KEY_REFRESH:
5107             erase();
5108
5109             /* FALLTHRU */
5110         case '?':
5111             if (c == '?')
5112                 show_panner_legend = !show_panner_legend;
5113             panner_legend(LINES - 4);
5114             panner_legend(LINES - 3);
5115             panner_legend(LINES - 2);
5116             panner_legend(LINES - 1);
5117             break;
5118         case 'a':
5119             pan_lines = !pan_lines;
5120             fill_pad(pad, pan_lines);
5121             pending_pan = FALSE;
5122             break;
5123
5124 #if HAVE_GETTIMEOFDAY
5125         case 't':
5126             timing = !timing;
5127             if (!timing)
5128                 panner_legend(LINES - 1);
5129             break;
5130 #endif
5131         case 's':
5132             scrollers = !scrollers;
5133             break;
5134
5135             /* Move the top-left corner of the pad, keeping the bottom-right
5136              * corner fixed.
5137              */
5138         case 'h':               /* increase-columns: move left edge to left */
5139             if (top_x <= 0)
5140                 beep();
5141             else {
5142                 panner_v_cleanup(top_y, top_x, porty);
5143                 top_x--;
5144             }
5145             break;
5146
5147         case 'j':               /* decrease-lines: move top-edge down */
5148             if (top_y >= porty)
5149                 beep();
5150             else {
5151                 panner_h_cleanup(top_y - 1, top_x - (top_x > 0), portx);
5152                 top_y++;
5153             }
5154             break;
5155
5156         case 'k':               /* increase-lines: move top-edge up */
5157             if (top_y <= 0)
5158                 beep();
5159             else {
5160                 top_y--;
5161                 panner_h_cleanup(top_y, top_x, portx);
5162             }
5163             break;
5164
5165         case 'l':               /* decrease-columns: move left-edge to right */
5166             if (top_x >= portx)
5167                 beep();
5168             else {
5169                 panner_v_cleanup(top_y - (top_y > 0), top_x - 1, porty);
5170                 top_x++;
5171             }
5172             break;
5173
5174             /* Move the bottom-right corner of the pad, keeping the top-left
5175              * corner fixed.
5176              */
5177         case KEY_IC:            /* increase-columns: move right-edge to right */
5178             if (portx >= pxmax || portx >= COLS)
5179                 beep();
5180             else {
5181                 panner_v_cleanup(top_y - (top_y > 0), portx - 1, porty);
5182                 ++portx;
5183             }
5184             break;
5185
5186         case KEY_IL:            /* increase-lines: move bottom-edge down */
5187             if (porty >= pymax || porty >= LINES)
5188                 beep();
5189             else {
5190                 panner_h_cleanup(porty - 1, top_x - (top_x > 0), portx);
5191                 ++porty;
5192             }
5193             break;
5194
5195         case KEY_DC:            /* decrease-columns: move bottom edge up */
5196             if (portx <= top_x)
5197                 beep();
5198             else {
5199                 portx--;
5200                 panner_v_cleanup(top_y - (top_y > 0), portx, porty);
5201             }
5202             break;
5203
5204         case KEY_DL:            /* decrease-lines */
5205             if (porty <= top_y)
5206                 beep();
5207             else {
5208                 porty--;
5209                 panner_h_cleanup(porty, top_x - (top_x > 0), portx);
5210             }
5211             break;
5212
5213         case KEY_LEFT:          /* pan leftwards */
5214             if (basex > 0)
5215                 basex--;
5216             else
5217                 beep();
5218             break;
5219
5220         case KEY_RIGHT: /* pan rightwards */
5221             if (basex + portx - (pymax > porty) < pxmax)
5222                 basex++;
5223             else
5224                 beep();
5225             break;
5226
5227         case KEY_UP:            /* pan upwards */
5228             if (basey > 0)
5229                 basey--;
5230             else
5231                 beep();
5232             break;
5233
5234         case KEY_DOWN:          /* pan downwards */
5235             if (basey + porty - (pxmax > portx) < pymax)
5236                 basey++;
5237             else
5238                 beep();
5239             break;
5240
5241         case 'H':
5242         case KEY_HOME:
5243         case KEY_FIND:
5244             basey = 0;
5245             break;
5246
5247         case 'E':
5248         case KEY_END:
5249         case KEY_SELECT:
5250             basey = pymax - porty;
5251             if (basey < 0)
5252                 basey = 0;
5253             break;
5254
5255         default:
5256             beep();
5257             break;
5258         }
5259
5260         MvAddCh(top_y - 1, top_x - 1, ACS_ULCORNER);
5261         do_v_line(top_y, top_x - 1, ACS_VLINE, porty);
5262         do_h_line(top_y - 1, top_x, ACS_HLINE, portx);
5263
5264         if (scrollers && (pxmax > portx - 1)) {
5265             int length = (portx - top_x - 1);
5266             float ratio = ((float) length) / ((float) pxmax);
5267
5268             lowend = (int) ((float) top_x + ((float) basex * ratio));
5269             highend = (int) ((float) top_x + ((float) (basex + length) * ratio));
5270
5271             do_h_line(porty - 1, top_x, ACS_HLINE, lowend);
5272             if (highend < portx) {
5273                 attron(A_REVERSE);
5274                 do_h_line(porty - 1, lowend, ' ', highend + 1);
5275                 attroff(A_REVERSE);
5276                 do_h_line(porty - 1, highend + 1, ACS_HLINE, portx);
5277             }
5278         } else
5279             do_h_line(porty - 1, top_x, ACS_HLINE, portx);
5280
5281         if (scrollers && (pymax > porty - 1)) {
5282             int length = (porty - top_y - 1);
5283             float ratio = ((float) length) / ((float) pymax);
5284
5285             lowend = (int) ((float) top_y + ((float) basey * ratio));
5286             highend = (int) ((float) top_y + ((float) (basey + length) * ratio));
5287
5288             do_v_line(top_y, portx - 1, ACS_VLINE, lowend);
5289             if (highend < porty) {
5290                 attron(A_REVERSE);
5291                 do_v_line(lowend, portx - 1, ' ', highend + 1);
5292                 attroff(A_REVERSE);
5293                 do_v_line(highend + 1, portx - 1, ACS_VLINE, porty);
5294             }
5295         } else
5296             do_v_line(top_y, portx - 1, ACS_VLINE, porty);
5297
5298         MvAddCh(top_y - 1, portx - 1, ACS_URCORNER);
5299         MvAddCh(porty - 1, top_x - 1, ACS_LLCORNER);
5300         MvAddCh(porty - 1, portx - 1, ACS_LRCORNER);
5301
5302         if (!pending_pan) {
5303 #if HAVE_GETTIMEOFDAY
5304             gettimeofday(&before, 0);
5305 #endif
5306             wnoutrefresh(stdscr);
5307
5308             pnoutrefresh(pad,
5309                          basey, basex,
5310                          top_y, top_x,
5311                          porty - (pxmax > portx) - 1,
5312                          portx - (pymax > porty) - 1);
5313
5314             doupdate();
5315 #if HAVE_GETTIMEOFDAY
5316 #define TIMEVAL2S(data) ((double) data.tv_sec + ((double) data.tv_usec / 1.0e6))
5317             if (timing) {
5318                 double elapsed;
5319                 gettimeofday(&after, 0);
5320                 elapsed = (TIMEVAL2S(after) - TIMEVAL2S(before));
5321                 move(LINES - 1, COLS - 12);
5322                 printw("Secs: %2.03f", elapsed);
5323                 refresh();
5324             }
5325 #endif
5326         }
5327
5328     } while
5329         ((c = pgetc(pad)) != KEY_EXIT);
5330
5331     scrollok(stdscr, TRUE);     /* reset to driver's default */
5332 }
5333
5334 static int
5335 padgetch(WINDOW *win)
5336 {
5337     static int count;
5338     static int last;
5339     int c;
5340
5341     if ((pending_pan = (count > 0)) != FALSE) {
5342         count--;
5343         pending_pan = (count != 0);
5344     } else {
5345         for (;;) {
5346             switch (c = wGetchar(win)) {
5347             case '!':
5348                 ShellOut(FALSE);
5349                 /* FALLTHRU */
5350             case CTRL('r'):
5351                 endwin();
5352                 refresh();
5353                 c = KEY_REFRESH;
5354                 break;
5355             case CTRL('l'):
5356                 c = KEY_REFRESH;
5357                 break;
5358             case 'U':
5359                 c = KEY_UP;
5360                 break;
5361             case 'D':
5362                 c = KEY_DOWN;
5363                 break;
5364             case 'R':
5365                 c = KEY_RIGHT;
5366                 break;
5367             case 'L':
5368                 c = KEY_LEFT;
5369                 break;
5370             case '+':
5371                 c = KEY_IL;
5372                 break;
5373             case '-':
5374                 c = KEY_DL;
5375                 break;
5376             case '>':
5377                 c = KEY_IC;
5378                 break;
5379             case '<':
5380                 c = KEY_DC;
5381                 break;
5382             case ERR:           /* FALLTHRU */
5383             case case_QUIT:
5384                 count = 0;
5385                 c = KEY_EXIT;
5386                 break;
5387             default:
5388                 if (c >= '0' && c <= '9') {
5389                     count = count * 10 + (c - '0');
5390                     continue;
5391                 }
5392                 break;
5393             }
5394             last = c;
5395             break;
5396         }
5397         if (count > 0)
5398             count--;
5399     }
5400     return (last);
5401 }
5402
5403 #define PAD_HIGH 200
5404 #define PAD_WIDE 200
5405
5406 static void
5407 demo_pad(void)
5408 /* Demonstrate pads. */
5409 {
5410     WINDOW *panpad = newpad(PAD_HIGH, PAD_WIDE);
5411
5412     if (panpad == 0) {
5413         Cannot("cannot create requested pad");
5414         return;
5415     }
5416
5417     fill_pad(panpad, FALSE);
5418
5419     panner_legend(LINES - 4);
5420     panner_legend(LINES - 3);
5421     panner_legend(LINES - 2);
5422     panner_legend(LINES - 1);
5423
5424     keypad(panpad, TRUE);
5425
5426     /* Make the pad (initially) narrow enough that a trace file won't wrap.
5427      * We'll still be able to widen it during a test, since that's required
5428      * for testing boundaries.
5429      */
5430     panner(panpad, 2, 2, LINES - 5, COLS - 15, padgetch);
5431
5432     delwin(panpad);
5433     endwin();
5434     erase();
5435 }
5436
5437 /****************************************************************************
5438  *
5439  * Tests from John Burnell's PDCurses tester
5440  *
5441  ****************************************************************************/
5442
5443 static void
5444 Continue(WINDOW *win)
5445 {
5446     noecho();
5447     wmove(win, 10, 1);
5448     MvWAddStr(win, 10, 1, " Press any key to continue");
5449     wrefresh(win);
5450     wGetchar(win);
5451 }
5452
5453 static void
5454 flushinp_test(WINDOW *win)
5455 /* Input test, adapted from John Burnell's PDCurses tester */
5456 {
5457     int w, h, bx, by, sw, sh, i;
5458
5459     WINDOW *subWin;
5460     wclear(win);
5461
5462     getmaxyx(win, h, w);
5463     getbegyx(win, by, bx);
5464     sw = w / 3;
5465     sh = h / 3;
5466     if ((subWin = subwin(win, sh, sw, by + h - sh - 2, bx + w - sw - 2)) == 0)
5467         return;
5468
5469 #ifdef A_COLOR
5470     if (use_colors) {
5471         init_pair(2, COLOR_CYAN, COLOR_BLUE);
5472         wbkgd(subWin, (chtype) (COLOR_PAIR(2) | ' '));
5473     }
5474 #endif
5475     (void) wattrset(subWin, A_BOLD);
5476     box(subWin, ACS_VLINE, ACS_HLINE);
5477     MvWAddStr(subWin, 2, 1, "This is a subwindow");
5478     wrefresh(win);
5479
5480     /*
5481      * This used to set 'nocbreak()'.  However, Alexander Lukyanov says that
5482      * it only happened to "work" on SVr4 because that implementation does not
5483      * emulate nocbreak+noecho mode, whereas ncurses does.  To get the desired
5484      * test behavior, we're using 'cbreak()', which will allow a single
5485      * character to return without needing a newline. - T.Dickey 1997/10/11.
5486      */
5487     cbreak();
5488     MvWAddStr(win, 0, 1, "This is a test of the flushinp() call.");
5489
5490     MvWAddStr(win, 2, 1, "Type random keys for 5 seconds.");
5491     MvWAddStr(win, 3, 1,
5492               "These should be discarded (not echoed) after the subwindow goes away.");
5493     wrefresh(win);
5494
5495     for (i = 0; i < 5; i++) {
5496         MvWPrintw(subWin, 1, 1, "Time = %d", i);
5497         wrefresh(subWin);
5498         napms(1000);
5499         flushinp();
5500     }
5501
5502     delwin(subWin);
5503     werase(win);
5504     flash();
5505     wrefresh(win);
5506     napms(1000);
5507
5508     MvWAddStr(win, 2, 1,
5509               "If you were still typing when the window timer expired,");
5510     MvWAddStr(win, 3, 1,
5511               "or else you typed nothing at all while it was running,");
5512     MvWAddStr(win, 4, 1,
5513               "test was invalid.  You'll see garbage or nothing at all. ");
5514     MvWAddStr(win, 6, 1, "Press a key");
5515     wmove(win, 9, 10);
5516     wrefresh(win);
5517     echo();
5518     wGetchar(win);
5519     flushinp();
5520     MvWAddStr(win, 12, 0,
5521               "If you see any key other than what you typed, flushinp() is broken.");
5522     Continue(win);
5523
5524     wmove(win, 9, 10);
5525     wdelch(win);
5526     wrefresh(win);
5527     wmove(win, 12, 0);
5528     clrtoeol();
5529     waddstr(win,
5530             "What you typed should now have been deleted; if not, wdelch() failed.");
5531     Continue(win);
5532
5533     cbreak();
5534 }
5535
5536 /****************************************************************************
5537  *
5538  * Menu test
5539  *
5540  ****************************************************************************/
5541
5542 #if USE_LIBMENU
5543
5544 #define MENU_Y  8
5545 #define MENU_X  8
5546
5547 static int
5548 menu_virtualize(int c)
5549 {
5550     if (c == '\n' || c == KEY_EXIT)
5551         return (MAX_COMMAND + 1);
5552     else if (c == 'u')
5553         return (REQ_SCR_ULINE);
5554     else if (c == 'd')
5555         return (REQ_SCR_DLINE);
5556     else if (c == 'b' || c == KEY_NPAGE)
5557         return (REQ_SCR_UPAGE);
5558     else if (c == 'f' || c == KEY_PPAGE)
5559         return (REQ_SCR_DPAGE);
5560     else if (c == 'n' || c == KEY_DOWN)
5561         return (REQ_NEXT_ITEM);
5562     else if (c == 'p' || c == KEY_UP)
5563         return (REQ_PREV_ITEM);
5564     else if (c == ' ')
5565         return (REQ_TOGGLE_ITEM);
5566     else {
5567         if (c != KEY_MOUSE)
5568             beep();
5569         return (c);
5570     }
5571 }
5572
5573 static CONST_MENUS char *animals[] =
5574 {
5575     "Lions",
5576     "Tigers",
5577     "Bears",
5578     "(Oh my!)",
5579     "Newts",
5580     "Platypi",
5581     "Lemurs",
5582     "(Oh really?!)",
5583     "Leopards",
5584     "Panthers",
5585     "Pumas",
5586     "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
5587     "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs, Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
5588     (char *) 0
5589 };
5590
5591 static void
5592 menu_test(void)
5593 {
5594     MENU *m;
5595     ITEM *items[SIZEOF(animals)];
5596     ITEM **ip = items;
5597     CONST_MENUS char **ap;
5598     int mrows, mcols, c;
5599     WINDOW *menuwin;
5600
5601 #ifdef NCURSES_MOUSE_VERSION
5602     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
5603 #endif
5604     MvAddStr(0, 0, "This is the menu test:");
5605     MvAddStr(2, 0, "  Use up and down arrow to move the select bar.");
5606     MvAddStr(3, 0, "  'n' and 'p' act like arrows.");
5607     MvAddStr(4, 0,
5608              "  'b' and 'f' scroll up/down (page), 'u' and 'd' (line).");
5609     MvAddStr(5, 0, "  Press return to exit.");
5610     refresh();
5611
5612     for (ap = animals; *ap; ap++) {
5613         if ((*ip = new_item(*ap, "")) != 0)
5614             ++ip;
5615     }
5616     *ip = (ITEM *) 0;
5617
5618     m = new_menu(items);
5619
5620     set_menu_format(m, (SIZEOF(animals) + 1) / 2, 1);
5621     scale_menu(m, &mrows, &mcols);
5622
5623     menuwin = newwin(mrows + 2, mcols + 2, MENU_Y, MENU_X);
5624     set_menu_win(m, menuwin);
5625     keypad(menuwin, TRUE);
5626     box(menuwin, 0, 0);
5627
5628     set_menu_sub(m, derwin(menuwin, mrows, mcols, 1, 1));
5629
5630     post_menu(m);
5631
5632     while ((c = menu_driver(m, menu_virtualize(wGetchar(menuwin)))) != E_UNKNOWN_COMMAND) {
5633         if (c == E_NOT_POSTED)
5634             break;
5635         if (c == E_REQUEST_DENIED)
5636             beep();
5637         continue;
5638     }
5639
5640     MvPrintw(LINES - 2, 0,
5641              "You chose: %s\n", item_name(current_item(m)));
5642     (void) addstr("Press any key to continue...");
5643     wGetchar(stdscr);
5644
5645     unpost_menu(m);
5646     delwin(menuwin);
5647
5648     free_menu(m);
5649     for (ip = items; *ip; ip++)
5650         free_item(*ip);
5651 #ifdef NCURSES_MOUSE_VERSION
5652     mousemask(0, (mmask_t *) 0);
5653 #endif
5654 }
5655
5656 #ifdef TRACE
5657 #define T_TBL(name) { #name, name }
5658 static struct {
5659     const char *name;
5660     unsigned mask;
5661 } t_tbl[] = {
5662
5663     T_TBL(TRACE_DISABLE),
5664         T_TBL(TRACE_TIMES),
5665         T_TBL(TRACE_TPUTS),
5666         T_TBL(TRACE_UPDATE),
5667         T_TBL(TRACE_MOVE),
5668         T_TBL(TRACE_CHARPUT),
5669         T_TBL(TRACE_ORDINARY),
5670         T_TBL(TRACE_CALLS),
5671         T_TBL(TRACE_VIRTPUT),
5672         T_TBL(TRACE_IEVENT),
5673         T_TBL(TRACE_BITS),
5674         T_TBL(TRACE_ICALLS),
5675         T_TBL(TRACE_CCALLS),
5676         T_TBL(TRACE_DATABASE),
5677         T_TBL(TRACE_ATTRS),
5678         T_TBL(TRACE_MAXIMUM),
5679     {
5680         (char *) 0, 0
5681     }
5682 };
5683
5684 static char *
5685 tracetrace(unsigned tlevel)
5686 {
5687     static char *buf;
5688     int n;
5689
5690     if (buf == 0) {
5691         size_t need = 12;
5692         for (n = 0; t_tbl[n].name != 0; n++)
5693             need += strlen(t_tbl[n].name) + 2;
5694         buf = typeMalloc(char, need);
5695         if (!buf)
5696             failed("tracetrace");
5697     }
5698     sprintf(buf, "0x%02x = {", tlevel);
5699     if (tlevel == 0) {
5700         sprintf(buf + strlen(buf), "%s, ", t_tbl[0].name);
5701     } else {
5702         for (n = 1; t_tbl[n].name != 0; n++)
5703             if ((tlevel & t_tbl[n].mask) == t_tbl[n].mask) {
5704                 strcat(buf, t_tbl[n].name);
5705                 strcat(buf, ", ");
5706             }
5707     }
5708     if (buf[strlen(buf) - 2] == ',')
5709         buf[strlen(buf) - 2] = '\0';
5710     return (strcat(buf, "}"));
5711 }
5712
5713 /* fake a dynamically reconfigurable menu using the 0th entry to deselect
5714  * the others
5715  */
5716 static int
5717 run_trace_menu(MENU * m)
5718 {
5719     ITEM **items;
5720     ITEM *i, **p;
5721
5722     for (;;) {
5723         bool changed = FALSE;
5724         switch (menu_driver(m, menu_virtualize(wGetchar(menu_win(m))))) {
5725         case E_UNKNOWN_COMMAND:
5726             return FALSE;
5727         default:
5728             items = menu_items(m);
5729             i = current_item(m);
5730             if (i == items[0]) {
5731                 if (item_value(i)) {
5732                     for (p = items + 1; *p != 0; p++)
5733                         if (item_value(*p)) {
5734                             set_item_value(*p, FALSE);
5735                             changed = TRUE;
5736                         }
5737                 }
5738             } else {
5739                 for (p = items + 1; *p != 0; p++)
5740                     if (item_value(*p)) {
5741                         set_item_value(items[0], FALSE);
5742                         changed = TRUE;
5743                         break;
5744                     }
5745             }
5746             if (!changed)
5747                 return TRUE;
5748         }
5749     }
5750 }
5751
5752 static void
5753 trace_set(void)
5754 /* interactively set the trace level */
5755 {
5756     MENU *m;
5757     ITEM *items[SIZEOF(t_tbl)];
5758     ITEM **ip = items;
5759     int mrows, mcols;
5760     unsigned newtrace;
5761     int n;
5762     WINDOW *menuwin;
5763
5764     MvAddStr(0, 0, "Interactively set trace level:");
5765     MvAddStr(2, 0, "  Press space bar to toggle a selection.");
5766     MvAddStr(3, 0, "  Use up and down arrow to move the select bar.");
5767     MvAddStr(4, 0, "  Press return to set the trace level.");
5768     MvPrintw(6, 0, "(Current trace level is %s)", tracetrace(_nc_tracing));
5769
5770     refresh();
5771
5772     for (n = 0; t_tbl[n].name != 0; n++) {
5773         if ((*ip = new_item(t_tbl[n].name, "")) != 0) {
5774             ++ip;
5775         }
5776     }
5777     *ip = (ITEM *) 0;
5778
5779     m = new_menu(items);
5780
5781     set_menu_format(m, 0, 2);
5782     scale_menu(m, &mrows, &mcols);
5783
5784     menu_opts_off(m, O_ONEVALUE);
5785     menuwin = newwin(mrows + 2, mcols + 2, MENU_Y, MENU_X);
5786     set_menu_win(m, menuwin);
5787     keypad(menuwin, TRUE);
5788     box(menuwin, 0, 0);
5789
5790     set_menu_sub(m, derwin(menuwin, mrows, mcols, 1, 1));
5791
5792     post_menu(m);
5793
5794     for (ip = menu_items(m); *ip; ip++) {
5795         unsigned mask = t_tbl[item_index(*ip)].mask;
5796         if (mask == 0)
5797             set_item_value(*ip, _nc_tracing == 0);
5798         else if ((mask & _nc_tracing) == mask)
5799             set_item_value(*ip, TRUE);
5800     }
5801
5802     while (run_trace_menu(m))
5803         continue;
5804
5805     newtrace = 0;
5806     for (ip = menu_items(m); *ip; ip++)
5807         if (item_value(*ip))
5808             newtrace |= t_tbl[item_index(*ip)].mask;
5809     trace(newtrace);
5810     Trace(("trace level interactively set to %s", tracetrace(_nc_tracing)));
5811
5812     MvPrintw(LINES - 2, 0,
5813              "Trace level is %s\n", tracetrace(_nc_tracing));
5814     (void) addstr("Press any key to continue...");
5815     wGetchar(stdscr);
5816
5817     unpost_menu(m);
5818     delwin(menuwin);
5819
5820     free_menu(m);
5821     for (ip = items; *ip; ip++)
5822         free_item(*ip);
5823 }
5824 #endif /* TRACE */
5825 #endif /* USE_LIBMENU */
5826
5827 /****************************************************************************
5828  *
5829  * Forms test
5830  *
5831  ****************************************************************************/
5832 #if USE_LIBFORM
5833 static FIELD *
5834 make_label(int frow, int fcol, NCURSES_CONST char *label)
5835 {
5836     FIELD *f = new_field(1, (int) strlen(label), frow, fcol, 0, 0);
5837
5838     if (f) {
5839         set_field_buffer(f, 0, label);
5840         set_field_opts(f, (int) ((unsigned) field_opts(f) & ~O_ACTIVE));
5841     }
5842     return (f);
5843 }
5844
5845 static FIELD *
5846 make_field(int frow, int fcol, int rows, int cols, bool secure)
5847 {
5848     FIELD *f = new_field(rows, cols, frow, fcol, 0, secure ? 1 : 0);
5849
5850     if (f) {
5851         set_field_back(f, A_UNDERLINE);
5852         set_field_userptr(f, (void *) 0);
5853     }
5854     return (f);
5855 }
5856
5857 static void
5858 display_form(FORM * f)
5859 {
5860     WINDOW *w;
5861     int rows, cols;
5862
5863     scale_form(f, &rows, &cols);
5864
5865     if ((w = newwin(rows + 2, cols + 4, 0, 0)) != (WINDOW *) 0) {
5866         set_form_win(f, w);
5867         set_form_sub(f, derwin(w, rows, cols, 1, 2));
5868         box(w, 0, 0);
5869         keypad(w, TRUE);
5870         if (post_form(f) != E_OK)
5871             wrefresh(w);
5872     }
5873 }
5874
5875 static void
5876 erase_form(FORM * f)
5877 {
5878     WINDOW *w = form_win(f);
5879     WINDOW *s = form_sub(f);
5880
5881     unpost_form(f);
5882     werase(w);
5883     wrefresh(w);
5884     delwin(s);
5885     delwin(w);
5886 }
5887
5888 static int
5889 edit_secure(FIELD * me, int c)
5890 {
5891     int rows, cols, frow, fcol, nrow, nbuf;
5892
5893     if (field_info(me, &rows, &cols, &frow, &fcol, &nrow, &nbuf) == E_OK
5894         && nbuf > 0) {
5895         char *source = field_buffer(me, 1);
5896         size_t have = (source ? strlen(source) : 0) + 1;
5897         size_t need = 80 + have;
5898         char *temp = malloc(need);
5899         size_t len;
5900
5901         if (temp != 0) {
5902             strncpy(temp, source ? source : "", have + 1);
5903             len = (size_t) (char *) field_userptr(me);
5904             if (c <= KEY_MAX) {
5905                 if (isgraph(c) && (len + 1) < sizeof(temp)) {
5906                     temp[len++] = (char) c;
5907                     temp[len] = 0;
5908                     set_field_buffer(me, 1, temp);
5909                     c = '*';
5910                 } else {
5911                     c = 0;
5912                 }
5913             } else {
5914                 switch (c) {
5915                 case REQ_BEG_FIELD:
5916                 case REQ_CLR_EOF:
5917                 case REQ_CLR_EOL:
5918                 case REQ_DEL_LINE:
5919                 case REQ_DEL_WORD:
5920                 case REQ_DOWN_CHAR:
5921                 case REQ_END_FIELD:
5922                 case REQ_INS_CHAR:
5923                 case REQ_INS_LINE:
5924                 case REQ_LEFT_CHAR:
5925                 case REQ_NEW_LINE:
5926                 case REQ_NEXT_WORD:
5927                 case REQ_PREV_WORD:
5928                 case REQ_RIGHT_CHAR:
5929                 case REQ_UP_CHAR:
5930                     c = 0;      /* we don't want to do inline editing */
5931                     break;
5932                 case REQ_CLR_FIELD:
5933                     if (len) {
5934                         temp[0] = 0;
5935                         set_field_buffer(me, 1, temp);
5936                     }
5937                     break;
5938                 case REQ_DEL_CHAR:
5939                 case REQ_DEL_PREV:
5940                     if (len) {
5941                         temp[--len] = 0;
5942                         set_field_buffer(me, 1, temp);
5943                     }
5944                     break;
5945                 }
5946             }
5947             set_field_userptr(me, (void *) len);
5948             free(temp);
5949         }
5950     }
5951     return c;
5952 }
5953
5954 static int
5955 form_virtualize(FORM * f, WINDOW *w)
5956 {
5957     /* *INDENT-OFF* */
5958     static const struct {
5959         int code;
5960         int result;
5961     } lookup[] = {
5962         { CTRL('A'),    REQ_NEXT_CHOICE },
5963         { CTRL('B'),    REQ_PREV_WORD },
5964         { CTRL('C'),    REQ_CLR_EOL },
5965         { CTRL('D'),    REQ_DOWN_FIELD },
5966         { CTRL('E'),    REQ_END_FIELD },
5967         { CTRL('F'),    REQ_NEXT_PAGE },
5968         { CTRL('G'),    REQ_DEL_WORD },
5969         { CTRL('H'),    REQ_DEL_PREV },
5970         { CTRL('I'),    REQ_INS_CHAR },
5971         { CTRL('K'),    REQ_CLR_EOF },
5972         { CTRL('L'),    REQ_LEFT_FIELD },
5973         { CTRL('M'),    REQ_NEW_LINE },
5974         { CTRL('N'),    REQ_NEXT_FIELD },
5975         { CTRL('O'),    REQ_INS_LINE },
5976         { CTRL('P'),    REQ_PREV_FIELD },
5977         { CTRL('R'),    REQ_RIGHT_FIELD },
5978         { CTRL('S'),    REQ_BEG_FIELD },
5979         { CTRL('U'),    REQ_UP_FIELD },
5980         { CTRL('V'),    REQ_DEL_CHAR },
5981         { CTRL('W'),    REQ_NEXT_WORD },
5982         { CTRL('X'),    REQ_CLR_FIELD },
5983         { CTRL('Y'),    REQ_DEL_LINE },
5984         { CTRL('Z'),    REQ_PREV_CHOICE },
5985         { ESCAPE,       MAX_FORM_COMMAND + 1 },
5986         { KEY_BACKSPACE, REQ_DEL_PREV },
5987         { KEY_DOWN,     REQ_DOWN_CHAR },
5988         { KEY_END,      REQ_LAST_FIELD },
5989         { KEY_HOME,     REQ_FIRST_FIELD },
5990         { KEY_LEFT,     REQ_LEFT_CHAR },
5991         { KEY_LL,       REQ_LAST_FIELD },
5992         { KEY_NEXT,     REQ_NEXT_FIELD },
5993         { KEY_NPAGE,    REQ_NEXT_PAGE },
5994         { KEY_PPAGE,    REQ_PREV_PAGE },
5995         { KEY_PREVIOUS, REQ_PREV_FIELD },
5996         { KEY_RIGHT,    REQ_RIGHT_CHAR },
5997         { KEY_UP,       REQ_UP_CHAR },
5998         { QUIT,         MAX_FORM_COMMAND + 1 }
5999     };
6000     /* *INDENT-ON* */
6001
6002     static int mode = REQ_INS_MODE;
6003     int c = wGetchar(w);
6004     unsigned n;
6005     FIELD *me = current_field(f);
6006     bool current = TRUE;
6007
6008     if (c == CTRL(']')) {
6009         if (mode == REQ_INS_MODE) {
6010             mode = REQ_OVL_MODE;
6011         } else {
6012             mode = REQ_INS_MODE;
6013         }
6014         c = mode;
6015     } else {
6016         for (n = 0; n < SIZEOF(lookup); n++) {
6017             if (lookup[n].code == c) {
6018                 c = lookup[n].result;
6019                 break;
6020             }
6021         }
6022     }
6023     MvPrintw(0, COLS - 6, "(%s)", mode == REQ_INS_MODE ? "INS" : "OVL");
6024
6025     /*
6026      * Force the field that the user is typing into to be in reverse video,
6027      * while the other fields are shown underlined.
6028      */
6029     switch (c) {
6030     case REQ_BEG_FIELD:
6031     case REQ_CLR_EOF:
6032     case REQ_CLR_EOL:
6033     case REQ_CLR_FIELD:
6034     case REQ_DEL_CHAR:
6035     case REQ_DEL_LINE:
6036     case REQ_DEL_PREV:
6037     case REQ_DEL_WORD:
6038     case REQ_END_FIELD:
6039     case REQ_INS_CHAR:
6040     case REQ_INS_LINE:
6041     case REQ_LEFT_CHAR:
6042     case REQ_LEFT_FIELD:
6043     case REQ_NEXT_WORD:
6044     case REQ_RIGHT_CHAR:
6045         current = TRUE;
6046         break;
6047     default:
6048         current = (c < KEY_MAX);
6049         break;
6050     }
6051     if (current) {
6052         c = edit_secure(me, c);
6053         set_field_back(me, A_REVERSE);
6054     } else {
6055         c = edit_secure(me, c);
6056         set_field_back(me, A_UNDERLINE);
6057     }
6058     return c;
6059 }
6060
6061 static int
6062 my_form_driver(FORM * form, int c)
6063 {
6064     if (c == (MAX_FORM_COMMAND + 1)
6065         && form_driver(form, REQ_VALIDATION) == E_OK)
6066         return (TRUE);
6067     else {
6068         beep();
6069         return (FALSE);
6070     }
6071 }
6072
6073 #ifdef NCURSES_VERSION
6074 #define FIELDCHECK_CB(func) bool func(FIELD * fld, const void * data GCC_UNUSED)
6075 #define CHAR_CHECK_CB(func) bool func(int ch, const void *data GCC_UNUSED)
6076 #else
6077 #define FIELDCHECK_CB(func) int func(FIELD * fld, char * data GCC_UNUSED)
6078 #define CHAR_CHECK_CB(func) int func(int ch, char *data GCC_UNUSED)
6079 #endif
6080
6081 /*
6082  * Allow a middle initial, optionally with a '.' to end it.
6083  */
6084 static
6085 FIELDCHECK_CB(mi_field_check)
6086 {
6087     char *s = field_buffer(fld, 0);
6088     int state = 0;
6089     int n;
6090
6091     for (n = 0; s[n] != '\0'; ++n) {
6092         switch (state) {
6093         case 0:
6094             if (s[n] == '.') {
6095                 if (n != 1)
6096                     return FALSE;
6097                 state = 2;
6098             } else if (isspace(UChar(s[n]))) {
6099                 state = 2;
6100             }
6101             break;
6102         case 2:
6103             if (!isspace(UChar(s[n])))
6104                 return FALSE;
6105             break;
6106         }
6107     }
6108
6109     /* force the form to display a leading capital */
6110     if (islower(UChar(s[0]))) {
6111         s[0] = (char) toupper(UChar(s[0]));
6112         set_field_buffer(fld, 0, s);
6113     }
6114     return TRUE;
6115 }
6116
6117 static
6118 CHAR_CHECK_CB(mi_char_check)
6119 {
6120     return ((isalpha(ch) || ch == '.') ? TRUE : FALSE);
6121 }
6122
6123 /*
6124  * Passwords should be at least 6 characters.
6125  */
6126 static
6127 FIELDCHECK_CB(pw_field_check)
6128 {
6129     char *s = field_buffer(fld, 0);
6130     int n;
6131
6132     for (n = 0; s[n] != '\0'; ++n) {
6133         if (isspace(UChar(s[n]))) {
6134             if (n < 6)
6135                 return FALSE;
6136         }
6137     }
6138     return TRUE;
6139 }
6140
6141 static
6142 CHAR_CHECK_CB(pw_char_check)
6143 {
6144     return (isgraph(ch) ? TRUE : FALSE);
6145 }
6146
6147 static void
6148 demo_forms(void)
6149 {
6150     WINDOW *w;
6151     FORM *form;
6152     FIELD *f[12], *secure;
6153     FIELDTYPE *fty_middle = new_fieldtype(mi_field_check, mi_char_check);
6154     FIELDTYPE *fty_passwd = new_fieldtype(pw_field_check, pw_char_check);
6155     int finished = 0, c;
6156     unsigned n = 0;
6157
6158 #ifdef NCURSES_MOUSE_VERSION
6159     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
6160 #endif
6161
6162     move(18, 0);
6163     addstr("Defined edit/traversal keys:   ^Q/ESC- exit form\n");
6164     addstr("^N   -- go to next field       ^P  -- go to previous field\n");
6165     addstr("Home -- go to first field      End -- go to last field\n");
6166     addstr("^L   -- go to field to left    ^R  -- go to field to right\n");
6167     addstr("^U   -- move upward to field   ^D  -- move downward to field\n");
6168     addstr("^W   -- go to next word        ^B  -- go to previous word\n");
6169     addstr("^S   -- go to start of field   ^E  -- go to end of field\n");
6170     addstr("^H   -- delete previous char   ^Y  -- delete line\n");
6171     addstr("^G   -- delete current word    ^C  -- clear to end of line\n");
6172     addstr("^K   -- clear to end of field  ^X  -- clear field\n");
6173     addstr("Arrow keys move within a field as you would expect. ^] toggles overlay mode.");
6174
6175     MvAddStr(4, 57, "Forms Entry Test");
6176
6177     refresh();
6178
6179     /* describe the form */
6180     memset(f, 0, sizeof(f));
6181     f[n++] = make_label(0, 15, "Sample Form");
6182
6183     f[n++] = make_label(2, 0, "Last Name");
6184     f[n++] = make_field(3, 0, 1, 18, FALSE);
6185     set_field_type(f[n - 1], TYPE_ALPHA, 1);
6186
6187     f[n++] = make_label(2, 20, "First Name");
6188     f[n++] = make_field(3, 20, 1, 12, FALSE);
6189     set_field_type(f[n - 1], TYPE_ALPHA, 1);
6190
6191     f[n++] = make_label(2, 34, "Middle Name");
6192     f[n++] = make_field(3, 34, 1, 12, FALSE);
6193     set_field_type(f[n - 1], fty_middle);
6194
6195     f[n++] = make_label(5, 0, "Comments");
6196     f[n++] = make_field(6, 0, 4, 46, FALSE);
6197
6198     f[n++] = make_label(5, 20, "Password:");
6199     secure =
6200         f[n++] = make_field(5, 30, 1, 9, TRUE);
6201     set_field_type(f[n - 1], fty_passwd);
6202     f[n] = (FIELD *) 0;
6203
6204     if ((form = new_form(f)) != 0) {
6205
6206         display_form(form);
6207
6208         w = form_win(form);
6209         raw();
6210         nonl();                 /* lets us read ^M's */
6211         while (!finished) {
6212             switch (form_driver(form, c = form_virtualize(form, w))) {
6213             case E_OK:
6214                 MvAddStr(5, 57, field_buffer(secure, 1));
6215                 clrtoeol();
6216                 refresh();
6217                 break;
6218             case E_UNKNOWN_COMMAND:
6219                 finished = my_form_driver(form, c);
6220                 break;
6221             default:
6222                 beep();
6223                 break;
6224             }
6225         }
6226
6227         erase_form(form);
6228
6229         free_form(form);
6230     }
6231     for (c = 0; f[c] != 0; c++)
6232         free_field(f[c]);
6233     free_fieldtype(fty_middle);
6234     free_fieldtype(fty_passwd);
6235     noraw();
6236     nl();
6237
6238 #ifdef NCURSES_MOUSE_VERSION
6239     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
6240 #endif
6241 }
6242 #endif /* USE_LIBFORM */
6243
6244 /****************************************************************************
6245  *
6246  * Overlap test
6247  *
6248  ****************************************************************************/
6249
6250 static void
6251 fillwin(WINDOW *win, char ch)
6252 {
6253     int y, x;
6254     int y1, x1;
6255
6256     getmaxyx(win, y1, x1);
6257     for (y = 0; y < y1; y++) {
6258         wmove(win, y, 0);
6259         for (x = 0; x < x1; x++)
6260             waddch(win, UChar(ch));
6261     }
6262 }
6263
6264 static void
6265 crosswin(WINDOW *win, char ch)
6266 {
6267     int y, x;
6268     int y1, x1;
6269
6270     getmaxyx(win, y1, x1);
6271     for (y = 0; y < y1; y++) {
6272         for (x = 0; x < x1; x++)
6273             if (((x > (x1 - 1) / 3) && (x <= (2 * (x1 - 1)) / 3))
6274                 || (((y > (y1 - 1) / 3) && (y <= (2 * (y1 - 1)) / 3)))) {
6275                 wmove(win, y, x);
6276                 waddch(win, UChar(ch));
6277             }
6278     }
6279 }
6280
6281 #define OVERLAP_FLAVORS 5
6282
6283 static void
6284 overlap_helpitem(int state, int item, char *message)
6285 {
6286     int row = (item / 2);
6287     int col = ((item % 2) ? COLS / 2 : 0);
6288
6289     move(LINES - 6 + row, col);
6290     printw("%c%c = %s", state == row ? '>' : ' ', 'a' + item, message);
6291     clrtoeol();
6292 }
6293
6294 static void
6295 overlap_test_1_attr(WINDOW *win, int flavor, int col)
6296 {
6297     NCURSES_PAIRS_T cpair = (NCURSES_PAIRS_T) (1 + (flavor * 2) + col);
6298
6299     switch (flavor) {
6300     case 0:
6301         (void) wattrset(win, A_NORMAL);
6302         break;
6303     case 1:
6304         (void) wattrset(win, A_BOLD);
6305         break;
6306     case 2:
6307         init_pair(cpair, COLOR_BLUE, COLOR_WHITE);
6308         (void) wattrset(win, (int) (COLOR_PAIR(cpair) | A_NORMAL));
6309         break;
6310     case 3:
6311         init_pair(cpair, COLOR_WHITE, COLOR_BLUE);
6312         (void) wattrset(win, (int) (COLOR_PAIR(cpair) | A_BOLD));
6313         break;
6314     }
6315 }
6316
6317 static void
6318 overlap_test_2_attr(WINDOW *win, int flavor, int col)
6319 {
6320     NCURSES_PAIRS_T cpair = (NCURSES_PAIRS_T) (9 + (flavor * 2) + col);
6321
6322     switch (flavor) {
6323     case 0:
6324         /* no effect */
6325         break;
6326     case 1:
6327         /* no effect */
6328         break;
6329     case 2:
6330         init_pair(cpair, COLOR_RED, COLOR_GREEN);
6331         wbkgdset(win, colored_chtype(' ', A_BLINK, cpair));
6332         break;
6333     case 3:
6334         wbkgdset(win, ' ' | A_NORMAL);
6335         break;
6336     }
6337 }
6338
6339 static int
6340 overlap_help(int state, int flavors[OVERLAP_FLAVORS])
6341 {
6342     int row;
6343     int col;
6344     int item;
6345     const char *ths, *tht;
6346     char msg[80];
6347
6348     if (state < 0)
6349         state += OVERLAP_FLAVORS;
6350     state = state % OVERLAP_FLAVORS;
6351     assert(state >= 0 && state < OVERLAP_FLAVORS);
6352
6353     for (item = 0; item < (2 * OVERLAP_FLAVORS); ++item) {
6354         row = item / 2;
6355         col = item % 2;
6356         ths = col ? "B" : "A";
6357         tht = col ? "A" : "B";
6358
6359         switch (row) {
6360         case 0:
6361             flavors[row] = 0;
6362             sprintf(msg, "refresh %s, then %s, then doupdate.", ths, tht);
6363             break;
6364         case 1:
6365             if (use_colors) {
6366                 flavors[row] %= 4;
6367             } else {
6368                 flavors[row] %= 2;
6369             }
6370             overlap_test_1_attr(stdscr, flavors[row], col);
6371             sprintf(msg, "fill window %s with letter %s.", ths, ths);
6372             break;
6373         case 2:
6374             if (use_colors) {
6375                 flavors[row] %= 4;
6376             } else {
6377                 flavors[row] %= 2;
6378             }
6379             switch (flavors[row]) {
6380             case 0:
6381                 sprintf(msg, "cross pattern in window %s.", ths);
6382                 break;
6383             case 1:
6384                 sprintf(msg, "draw box in window %s.", ths);
6385                 break;
6386             case 2:
6387                 sprintf(msg, "set background of window %s.", ths);
6388                 break;
6389             case 3:
6390                 sprintf(msg, "reset background of window %s.", ths);
6391                 break;
6392             }
6393             break;
6394         case 3:
6395             flavors[row] = 0;
6396             sprintf(msg, "clear window %s.", ths);
6397             break;
6398         case 4:
6399             flavors[row] %= 4;
6400             switch (flavors[row]) {
6401             case 0:
6402                 sprintf(msg, "overwrite %s onto %s.", ths, tht);
6403                 break;
6404             case 1:
6405                 sprintf(msg, "copywin(FALSE) %s onto %s.", ths, tht);
6406                 break;
6407             case 2:
6408                 sprintf(msg, "copywin(TRUE) %s onto %s.", ths, tht);
6409                 break;
6410             case 3:
6411                 sprintf(msg, "overlay %s onto %s.", ths, tht);
6412                 break;
6413             }
6414             break;
6415         }
6416         overlap_helpitem(state, item, msg);
6417         (void) wattrset(stdscr, A_NORMAL);
6418         wbkgdset(stdscr, ' ' | A_NORMAL);
6419     }
6420     move(LINES - 1, 0);
6421     printw("^Q/ESC = terminate test.  Up/down/space select test variations (%d %d).",
6422            state, flavors[state]);
6423
6424     return state;
6425 }
6426
6427 static void
6428 overlap_test_0(WINDOW *a, WINDOW *b)
6429 {
6430     touchwin(a);
6431     touchwin(b);
6432     wnoutrefresh(a);
6433     wnoutrefresh(b);
6434     doupdate();
6435 }
6436
6437 static void
6438 overlap_test_1(int flavor, int col, WINDOW *a, char fill)
6439 {
6440     overlap_test_1_attr(a, flavor, col);
6441     fillwin(a, fill);
6442     (void) wattrset(a, A_NORMAL);
6443 }
6444
6445 static void
6446 overlap_test_2(int flavor, int col, WINDOW *a, char fill)
6447 {
6448     overlap_test_2_attr(a, flavor, col);
6449     switch (flavor) {
6450     case 0:
6451         crosswin(a, fill);
6452         break;
6453     case 1:
6454         box(a, 0, 0);
6455         break;
6456     case 2:
6457         /* done in overlap_test_2_attr */
6458         break;
6459     case 3:
6460         /* done in overlap_test_2_attr */
6461         break;
6462     }
6463 }
6464
6465 static void
6466 overlap_test_3(WINDOW *a)
6467 {
6468     wclear(a);
6469     wmove(a, 0, 0);
6470 }
6471
6472 static void
6473 overlap_test_4(int flavor, WINDOW *a, WINDOW *b)
6474 {
6475     switch (flavor) {
6476     case 0:
6477         overwrite(a, b);
6478         break;
6479     case 1:
6480         copywin(a, b, 0, 0, 0, 0, getmaxy(b), getmaxx(b), FALSE);
6481         break;
6482     case 2:
6483         copywin(a, b, 0, 0, 0, 0, getmaxy(b), getmaxx(b), TRUE);
6484         break;
6485     case 3:
6486         overlay(a, b);
6487         break;
6488     }
6489 }
6490
6491 /* test effects of overlapping windows */
6492 static void
6493 overlap_test(void)
6494 {
6495     int ch;
6496     int state, flavor[OVERLAP_FLAVORS];
6497
6498     WINDOW *win1 = newwin(9, 20, 3, 3);
6499     WINDOW *win2 = newwin(9, 20, 9, 16);
6500
6501     curs_set(0);
6502     raw();
6503     refresh();
6504     move(0, 0);
6505     printw("This test shows the behavior of wnoutrefresh() with respect to\n");
6506     printw("the shared region of two overlapping windows A and B.  The cross\n");
6507     printw("pattern in each window does not overlap the other.\n");
6508
6509     memset(flavor, 0, sizeof(flavor));
6510     state = overlap_help(0, flavor);
6511
6512     while (!isQuit(ch = Getchar()))
6513         switch (ch) {
6514         case 'a':               /* refresh window A first, then B */
6515             overlap_test_0(win1, win2);
6516             break;
6517
6518         case 'b':               /* refresh window B first, then A */
6519             overlap_test_0(win2, win1);
6520             break;
6521
6522         case 'c':               /* fill window A so it's visible */
6523             overlap_test_1(flavor[1], 0, win1, 'A');
6524             break;
6525
6526         case 'd':               /* fill window B so it's visible */
6527             overlap_test_1(flavor[1], 1, win2, 'B');
6528             break;
6529
6530         case 'e':               /* cross test pattern in window A */
6531             overlap_test_2(flavor[2], 0, win1, 'A');
6532             break;
6533
6534         case 'f':               /* cross test pattern in window A */
6535             overlap_test_2(flavor[2], 1, win2, 'B');
6536             break;
6537
6538         case 'g':               /* clear window A */
6539             overlap_test_3(win1);
6540             break;
6541
6542         case 'h':               /* clear window B */
6543             overlap_test_3(win2);
6544             break;
6545
6546         case 'i':               /* overwrite A onto B */
6547             overlap_test_4(flavor[4], win1, win2);
6548             break;
6549
6550         case 'j':               /* overwrite B onto A */
6551             overlap_test_4(flavor[4], win2, win1);
6552             break;
6553
6554         case CTRL('n'):
6555         case KEY_DOWN:
6556             state = overlap_help(state + 1, flavor);
6557             break;
6558
6559         case CTRL('p'):
6560         case KEY_UP:
6561             state = overlap_help(state - 1, flavor);
6562             break;
6563
6564         case ' ':
6565             flavor[state] += 1;
6566             state = overlap_help(state, flavor);
6567             break;
6568
6569         case '?':
6570             state = overlap_help(state, flavor);
6571             break;
6572
6573         default:
6574             beep();
6575             break;
6576         }
6577
6578     delwin(win2);
6579     delwin(win1);
6580     erase();
6581     curs_set(1);
6582     endwin();
6583 }
6584
6585 /****************************************************************************
6586  *
6587  * Main sequence
6588  *
6589  ****************************************************************************/
6590
6591 static bool
6592 do_single_test(const char c)
6593 /* perform a single specified test */
6594 {
6595     switch (c) {
6596     case 'a':
6597         getch_test();
6598         break;
6599
6600 #if USE_WIDEC_SUPPORT
6601     case 'A':
6602         get_wch_test();
6603         break;
6604 #endif
6605
6606     case 'b':
6607         attr_test();
6608         break;
6609
6610 #if USE_WIDEC_SUPPORT
6611     case 'B':
6612         wide_attr_test();
6613         break;
6614 #endif
6615
6616     case 'c':
6617         if (!use_colors)
6618             Cannot("does not support color.");
6619         else
6620             color_test();
6621         break;
6622
6623 #if USE_WIDEC_SUPPORT
6624     case 'C':
6625         if (!use_colors)
6626             Cannot("does not support color.");
6627         else
6628             wide_color_test();
6629         break;
6630 #endif
6631
6632     case 'd':
6633         if (!use_colors)
6634             Cannot("does not support color.");
6635         else if (!can_change_color())
6636             Cannot("has hardwired color values.");
6637         else
6638             color_edit();
6639         break;
6640
6641 #if USE_SOFTKEYS
6642     case 'e':
6643         slk_test();
6644         break;
6645
6646 #if USE_WIDEC_SUPPORT
6647     case 'E':
6648         wide_slk_test();
6649         break;
6650 #endif
6651 #endif
6652
6653     case 'f':
6654         acs_display();
6655         break;
6656
6657 #if USE_WIDEC_SUPPORT
6658     case 'F':
6659         wide_acs_display();
6660         break;
6661 #endif
6662
6663 #if USE_LIBPANEL
6664     case 'o':
6665         demo_panels(init_panel, fill_panel);
6666         break;
6667 #endif
6668
6669 #if USE_WIDEC_SUPPORT && USE_LIBPANEL
6670     case 'O':
6671         demo_panels(init_wide_panel, fill_wide_panel);
6672         break;
6673 #endif
6674
6675     case 'g':
6676         acs_and_scroll();
6677         break;
6678
6679     case 'i':
6680         flushinp_test(stdscr);
6681         break;
6682
6683     case 'k':
6684         test_sgr_attributes();
6685         break;
6686
6687 #if USE_LIBMENU
6688     case 'm':
6689         menu_test();
6690         break;
6691 #endif
6692
6693     case 'p':
6694         demo_pad();
6695         break;
6696
6697 #if USE_LIBFORM
6698     case 'r':
6699         demo_forms();
6700         break;
6701 #endif
6702
6703     case 's':
6704         overlap_test();
6705         break;
6706
6707 #if USE_LIBMENU && defined(TRACE)
6708     case 't':
6709         trace_set();
6710         break;
6711 #endif
6712
6713     case '?':
6714         break;
6715
6716     default:
6717         return FALSE;
6718     }
6719
6720     return TRUE;
6721 }
6722
6723 static void
6724 usage(void)
6725 {
6726     static const char *const tbl[] =
6727     {
6728         "Usage: ncurses [options]"
6729         ,""
6730         ,"Options:"
6731 #ifdef NCURSES_VERSION
6732         ,"  -a f,b   set default-colors (assumed white-on-black)"
6733         ,"  -d       use default-colors if terminal supports them"
6734 #endif
6735         ,"  -E       call use_env(FALSE) to ignore $LINES and $COLUMNS"
6736 #if USE_SOFTKEYS
6737         ,"  -e fmt   specify format for soft-keys test (e)"
6738 #endif
6739 #if HAVE_RIPOFFLINE
6740         ,"  -f       rip-off footer line (can repeat)"
6741         ,"  -h       rip-off header line (can repeat)"
6742 #endif
6743         ,"  -m       do not use colors"
6744         ,"  -p file  rgb values to use in 'd' rather than ncurses's builtin"
6745 #if USE_LIBPANEL
6746         ,"  -s msec  specify nominal time for panel-demo (default: 1, to hold)"
6747 #endif
6748 #if defined(NCURSES_VERSION_PATCH) && (NCURSES_VERSION_PATCH >= 20120714) && !defined(__MINGW32__)
6749         ,"  -T       call use_tioctl(TRUE) to allow SIGWINCH to override environment"
6750 #endif
6751 #ifdef TRACE
6752         ,"  -t mask  specify default trace-level (may toggle with ^T)"
6753 #endif
6754     };
6755     size_t n;
6756     for (n = 0; n < SIZEOF(tbl); n++)
6757         fprintf(stderr, "%s\n", tbl[n]);
6758     ExitProgram(EXIT_FAILURE);
6759 }
6760
6761 static void
6762 set_terminal_modes(void)
6763 {
6764     noraw();
6765     cbreak();
6766     noecho();
6767     scrollok(stdscr, TRUE);
6768     idlok(stdscr, TRUE);
6769     keypad(stdscr, TRUE);
6770 }
6771
6772 #ifdef SIGUSR1
6773 static void
6774 announce_sig(int sig)
6775 {
6776     (void) fprintf(stderr, "Handled signal %d\r\n", sig);
6777 }
6778 #endif
6779
6780 #if HAVE_RIPOFFLINE
6781 static int
6782 rip_footer(WINDOW *win, int cols)
6783 {
6784     wbkgd(win, A_REVERSE);
6785     werase(win);
6786     wmove(win, 0, 0);
6787     wprintw(win, "footer: window %p, %d columns", (void *) win, cols);
6788     wnoutrefresh(win);
6789     return OK;
6790 }
6791
6792 static int
6793 rip_header(WINDOW *win, int cols)
6794 {
6795     wbkgd(win, A_REVERSE);
6796     werase(win);
6797     wmove(win, 0, 0);
6798     wprintw(win, "header: window %p, %d columns", (void *) win, cols);
6799     wnoutrefresh(win);
6800     return OK;
6801 }
6802 #endif /* HAVE_RIPOFFLINE */
6803
6804 static void
6805 main_menu(bool top)
6806 {
6807     char command;
6808
6809     do {
6810         (void) puts("This is the ncurses main menu");
6811         (void) puts("a = keyboard and mouse input test");
6812 #if USE_WIDEC_SUPPORT
6813         (void) puts("A = wide-character keyboard and mouse input test");
6814 #endif
6815         (void) puts("b = character attribute test");
6816 #if USE_WIDEC_SUPPORT
6817         (void) puts("B = wide-character attribute test");
6818 #endif
6819         (void) puts("c = color test pattern");
6820 #if USE_WIDEC_SUPPORT
6821         (void) puts("C = color test pattern using wide-character calls");
6822 #endif
6823         if (top)
6824             (void) puts("d = edit RGB color values");
6825 #if USE_SOFTKEYS
6826         (void) puts("e = exercise soft keys");
6827 #if USE_WIDEC_SUPPORT
6828         (void) puts("E = exercise soft keys using wide-characters");
6829 #endif
6830 #endif
6831         (void) puts("f = display ACS characters");
6832 #if USE_WIDEC_SUPPORT
6833         (void) puts("F = display Wide-ACS characters");
6834 #endif
6835         (void) puts("g = display windows and scrolling");
6836         (void) puts("i = test of flushinp()");
6837         (void) puts("k = display character attributes");
6838 #if USE_LIBMENU
6839         (void) puts("m = menu code test");
6840 #endif
6841 #if USE_LIBPANEL
6842         (void) puts("o = exercise panels library");
6843 #if USE_WIDEC_SUPPORT
6844         (void) puts("O = exercise panels with wide-characters");
6845 #endif
6846 #endif
6847         (void) puts("p = exercise pad features");
6848         (void) puts("q = quit");
6849 #if USE_LIBFORM
6850         (void) puts("r = exercise forms code");
6851 #endif
6852         (void) puts("s = overlapping-refresh test");
6853 #if USE_LIBMENU && defined(TRACE)
6854         (void) puts("t = set trace level");
6855 #endif
6856         (void) puts("? = repeat this command summary");
6857
6858         (void) fputs("> ", stdout);
6859         (void) fflush(stdout);  /* necessary under SVr4 curses */
6860
6861         /*
6862          * This used to be an 'fgets()' call (until 1996/10).  However with
6863          * some runtime libraries, mixing stream I/O and 'read()' causes the
6864          * input stream to be flushed when switching between the two.
6865          */
6866         command = 0;
6867         for (;;) {
6868             char ch = '\0';
6869             if (read(fileno(stdin), &ch, (size_t) 1) <= 0) {
6870                 if (command == 0)
6871                     command = 'q';
6872                 break;
6873             } else if (command == 0 && !isspace(UChar(ch))) {
6874                 command = ch;
6875             } else if (ch == '\n' || ch == '\r') {
6876                 if ((command == 'd') && !top) {
6877                     (void) fputs("Do not nest test-d\n", stdout);
6878                     command = 0;
6879                 }
6880                 if (command != 0)
6881                     break;
6882                 (void) fputs("> ", stdout);
6883                 (void) fflush(stdout);
6884             }
6885         }
6886
6887         if (do_single_test(command)) {
6888             /*
6889              * This may be overkill; it's intended to reset everything back
6890              * to the initial terminal modes so that tests don't get in
6891              * each other's way.
6892              */
6893             flushinp();
6894             set_terminal_modes();
6895             reset_prog_mode();
6896             clear();
6897             refresh();
6898             endwin();
6899             if (command == '?') {
6900                 (void) puts("This is the ncurses capability tester.");
6901                 (void)
6902                     puts("You may select a test from the main menu by typing the");
6903                 (void)
6904                     puts("key letter of the choice (the letter to left of the =)");
6905                 (void)
6906                     puts("at the > prompt.  Type `q' to exit.");
6907             }
6908             continue;
6909         }
6910     } while
6911         (command != 'q');
6912 }
6913
6914 /*+-------------------------------------------------------------------------
6915         main(argc,argv)
6916 --------------------------------------------------------------------------*/
6917
6918 #define okCOLOR(n) ((n) >= 0 && (n) < max_colors)
6919 #define okRGB(n)   ((n) >= 0 && (n) <= 1000)
6920
6921 int
6922 main(int argc, char *argv[])
6923 {
6924     int c;
6925     int my_e_param = 1;
6926 #ifdef NCURSES_VERSION
6927     int default_fg = COLOR_WHITE;
6928     int default_bg = COLOR_BLACK;
6929     bool assumed_colors = FALSE;
6930     bool default_colors = FALSE;
6931 #endif
6932     char *palette_file = 0;
6933     bool monochrome = FALSE;
6934
6935     setlocale(LC_ALL, "");
6936
6937     while ((c = getopt(argc, argv, "a:dEe:fhmp:s:Tt:")) != -1) {
6938         switch (c) {
6939 #ifdef NCURSES_VERSION
6940         case 'a':
6941             assumed_colors = TRUE;
6942             switch (sscanf(optarg, "%d,%d", &default_fg, &default_bg)) {
6943             case 0:
6944                 default_fg = COLOR_WHITE;
6945                 /* FALLTHRU */
6946             case 1:
6947                 default_bg = COLOR_BLACK;
6948                 break;
6949             }
6950             break;
6951         case 'd':
6952             default_colors = TRUE;
6953             break;
6954 #endif
6955         case 'E':
6956             use_env(FALSE);
6957             break;
6958         case 'e':
6959             my_e_param = atoi(optarg);
6960 #ifdef NCURSES_VERSION
6961             if (my_e_param > 3) /* allow extended layouts */
6962                 usage();
6963 #else
6964             if (my_e_param > 1)
6965                 usage();
6966 #endif
6967             break;
6968 #if HAVE_RIPOFFLINE
6969         case 'f':
6970             ripoffline(-1, rip_footer);
6971             break;
6972         case 'h':
6973             ripoffline(1, rip_header);
6974             break;
6975 #endif /* HAVE_RIPOFFLINE */
6976         case 'm':
6977             monochrome = TRUE;
6978             break;
6979         case 'p':
6980             palette_file = optarg;
6981             break;
6982 #if USE_LIBPANEL
6983         case 's':
6984             nap_msec = (int) atol(optarg);
6985             break;
6986 #endif
6987 #if defined(NCURSES_VERSION_PATCH) && (NCURSES_VERSION_PATCH >= 20120714) && !defined(__MINGW32__)
6988         case 'T':
6989             use_tioctl(TRUE);
6990             break;
6991 #endif
6992 #ifdef TRACE
6993         case 't':
6994             save_trace = (unsigned) strtol(optarg, 0, 0);
6995             break;
6996 #endif
6997         default:
6998             usage();
6999         }
7000     }
7001
7002     /*
7003      * If there's no menus (unlikely for ncurses!), then we'll have to set
7004      * tracing on initially, just in case the user wants to test something that
7005      * doesn't involve wGetchar.
7006      */
7007 #ifdef TRACE
7008     /* enable debugging */
7009 #if !USE_LIBMENU
7010     trace(save_trace);
7011 #else
7012     if (!isatty(fileno(stdin)))
7013         trace(save_trace);
7014 #endif /* USE_LIBMENU */
7015 #endif /* TRACE */
7016
7017 #if USE_SOFTKEYS
7018     /* tell it we're going to play with soft keys */
7019     slk_init(my_e_param);
7020 #endif
7021
7022 #ifdef SIGUSR1
7023     /* set up null signal catcher so we can see what interrupts to getch do */
7024     signal(SIGUSR1, announce_sig);
7025 #endif
7026
7027     /* we must initialize the curses data structure only once */
7028     initscr();
7029     bkgdset(BLANK);
7030
7031     /* tests, in general, will want these modes */
7032     use_colors = (bool) (monochrome ? FALSE : has_colors());
7033
7034     if (use_colors) {
7035         start_color();
7036 #ifdef NCURSES_VERSION_PATCH
7037         max_colors = COLORS;    /* was > 16 ? 16 : COLORS */
7038 #if HAVE_USE_DEFAULT_COLORS
7039         if (default_colors) {
7040             use_default_colors();
7041             min_colors = -1;
7042         }
7043 #if HAVE_ASSUME_DEFAULT_COLORS
7044         if (assumed_colors)
7045             assume_default_colors(default_fg, default_bg);
7046 #endif
7047 #endif
7048 #else /* normal SVr4 curses */
7049         max_colors = COLORS;    /* was > 8 ? 8 : COLORS */
7050 #endif
7051         max_pairs = COLOR_PAIRS;        /* was > 256 ? 256 : COLOR_PAIRS */
7052
7053         if (can_change_color()) {
7054             NCURSES_PAIRS_T cp;
7055             all_colors = typeMalloc(RGB_DATA, (unsigned) max_colors);
7056             if (!all_colors)
7057                 failed("all_colors");
7058             for (cp = 0; cp < max_colors; ++cp) {
7059                 color_content(cp,
7060                               &all_colors[cp].red,
7061                               &all_colors[cp].green,
7062                               &all_colors[cp].blue);
7063             }
7064             if (palette_file != 0) {
7065                 FILE *fp = fopen(palette_file, "r");
7066                 if (fp != 0) {
7067                     char buffer[BUFSIZ];
7068                     int red, green, blue;
7069                     int scale = 1000;
7070                     while (fgets(buffer, sizeof(buffer), fp) != 0) {
7071                         if (sscanf(buffer, "scale:%d", &c) == 1) {
7072                             scale = c;
7073                         } else if (sscanf(buffer, "%d:%d %d %d",
7074                                           &c,
7075                                           &red,
7076                                           &green,
7077                                           &blue) == 4
7078                                    && okCOLOR(c)
7079                                    && okRGB(red)
7080                                    && okRGB(green)
7081                                    && okRGB(blue)) {
7082 #define Scaled(n) (NCURSES_COLOR_T) (((n) * 1000) / scale)
7083                             all_colors[c].red = Scaled(red);
7084                             all_colors[c].green = Scaled(green);
7085                             all_colors[c].blue = Scaled(blue);
7086                         }
7087                     }
7088                     fclose(fp);
7089                 }
7090             }
7091         }
7092     }
7093     set_terminal_modes();
7094     def_prog_mode();
7095
7096     /*
7097      * Return to terminal mode, so we're guaranteed of being able to
7098      * select terminal commands even if the capabilities are wrong.
7099      */
7100     endwin();
7101
7102 #if HAVE_CURSES_VERSION
7103     (void) printf("Welcome to %s.  Press ? for help.\n", curses_version());
7104 #elif defined(NCURSES_VERSION_MAJOR) && defined(NCURSES_VERSION_MINOR) && defined(NCURSES_VERSION_PATCH)
7105     (void) printf("Welcome to ncurses %d.%d.%d.  Press ? for help.\n",
7106                   NCURSES_VERSION_MAJOR,
7107                   NCURSES_VERSION_MINOR,
7108                   NCURSES_VERSION_PATCH);
7109 #else
7110     (void) puts("Welcome to ncurses.  Press ? for help.");
7111 #endif
7112
7113     main_menu(TRUE);
7114
7115     ExitProgram(EXIT_SUCCESS);
7116 }
7117
7118 /* ncurses.c ends here */