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