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