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