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