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