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