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