]> ncurses.scripts.mit.edu Git - ncurses.git/blob - test/ncurses.c
326c747cad027cb542b0d50ae06e8ce1e3ad0cc1
[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.379 2012/11/18 00:56:44 tom Exp $
44
45 ***************************************************************************/
46
47 #include <test.priv.h>
48
49 #ifdef __hpux
50 #undef mvwdelch                 /* HPUX 11.23 macro will not compile */
51 #endif
52
53 #if HAVE_GETTIMEOFDAY
54 #if HAVE_SYS_TIME_H && HAVE_SYS_TIME_SELECT
55 #include <sys/time.h>
56 #endif
57 #if HAVE_SYS_SELECT_H
58 #include <sys/select.h>
59 #endif
60 #endif
61
62 #if USE_LIBPANEL
63 #include <panel.h>
64 #endif
65
66 #if USE_LIBMENU
67 #include <menu.h>
68 #endif
69
70 #if USE_LIBFORM
71 #include <form.h>
72 #endif
73
74 #ifdef NCURSES_VERSION
75
76 #define NCURSES_CONST_PARAM const void
77
78 #ifdef TRACE
79 static unsigned save_trace = TRACE_ORDINARY | TRACE_ICALLS | TRACE_CALLS;
80 extern unsigned _nc_tracing;
81 #endif
82
83 #else
84
85 #define NCURSES_CONST_PARAM char
86
87 #define mmask_t chtype          /* not specified in XSI */
88
89 #ifndef ACS_S3
90 #ifdef CURSES_ACS_ARRAY
91 #define ACS_S3          (CURSES_ACS_ARRAY['p'])         /* scan line 3 */
92 #define ACS_S7          (CURSES_ACS_ARRAY['r'])         /* scan line 7 */
93 #define ACS_LEQUAL      (CURSES_ACS_ARRAY['y'])         /* less/equal */
94 #define ACS_GEQUAL      (CURSES_ACS_ARRAY['z'])         /* greater/equal */
95 #define ACS_PI          (CURSES_ACS_ARRAY['{'])         /* Pi */
96 #define ACS_NEQUAL      (CURSES_ACS_ARRAY['|'])         /* not equal */
97 #define ACS_STERLING    (CURSES_ACS_ARRAY['}'])         /* UK pound sign */
98 #else
99 #define ACS_S3          (A_ALTCHARSET + 'p')    /* scan line 3 */
100 #define ACS_S7          (A_ALTCHARSET + 'r')    /* scan line 7 */
101 #define ACS_LEQUAL      (A_ALTCHARSET + 'y')    /* less/equal */
102 #define ACS_GEQUAL      (A_ALTCHARSET + 'z')    /* greater/equal */
103 #define ACS_PI          (A_ALTCHARSET + '{')    /* Pi */
104 #define ACS_NEQUAL      (A_ALTCHARSET + '|')    /* not equal */
105 #define ACS_STERLING    (A_ALTCHARSET + '}')    /* UK pound sign */
106 #endif
107 #endif /* ACS_S3 */
108
109 #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                 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     wrefresh(rwindow);
4248
4249     move(0, 0);
4250     clrtoeol();
4251     return (rwindow);
4252 }
4253
4254 static void
4255 newwin_move(FRAME * curp, int dy, int dx)
4256 {
4257     WINDOW *win = frame_win(curp);
4258     int cur_y, cur_x;
4259     int max_y, max_x;
4260
4261     getyx(win, cur_y, cur_x);
4262     getmaxyx(win, max_y, max_x);
4263     if ((cur_x += dx) < 0)
4264         cur_x = 0;
4265     else if (cur_x >= max_x)
4266         cur_x = max_x - 1;
4267     if ((cur_y += dy) < 0)
4268         cur_y = 0;
4269     else if (cur_y >= max_y)
4270         cur_y = max_y - 1;
4271     wmove(win, cur_y, cur_x);
4272 }
4273
4274 static FRAME *
4275 delete_framed(FRAME * fp, bool showit)
4276 {
4277     FRAME *np = 0;
4278
4279     if (fp != 0) {
4280         fp->last->next = fp->next;
4281         fp->next->last = fp->last;
4282
4283         if (showit) {
4284             werase(fp->wind);
4285             wrefresh(fp->wind);
4286         }
4287         delwin(fp->wind);
4288
4289         np = (fp == fp->next) ? 0 : fp->next;
4290         free(fp);
4291     }
4292     return np;
4293 }
4294
4295 static void
4296 acs_and_scroll(void)
4297 /* Demonstrate windows */
4298 {
4299     int c;
4300     FRAME *current = (FRAME *) 0, *neww;
4301     WINDOW *usescr;
4302 #if HAVE_PUTWIN && HAVE_GETWIN
4303     FILE *fp;
4304 #endif
4305
4306 #define DUMPFILE        "screendump"
4307
4308 #ifdef NCURSES_MOUSE_VERSION
4309     mousemask(BUTTON1_CLICKED, (mmask_t *) 0);
4310 #endif
4311     c = CTRL('C');
4312     raw();
4313     do {
4314         transient((FRAME *) 0, (char *) 0);
4315         switch (c) {
4316         case CTRL('C'):
4317             if ((neww = typeCalloc(FRAME, 1)) == 0) {
4318                 failed("acs_and_scroll");
4319                 goto breakout;
4320             }
4321             if ((neww->wind = getwindow()) == (WINDOW *) 0) {
4322                 failed("acs_and_scroll");
4323                 free(neww);
4324                 goto breakout;
4325             }
4326
4327             if (current == 0) { /* First element,  */
4328                 neww->next = neww;      /*   so point it at itself */
4329                 neww->last = neww;
4330             } else {
4331                 neww->next = current->next;
4332                 neww->last = current;
4333                 neww->last->next = neww;
4334                 neww->next->last = neww;
4335             }
4336             current = neww;
4337             /* SVr4 curses sets the keypad on all newly-created windows to
4338              * false.  Someone reported that PDCurses makes new windows inherit
4339              * this flag.  Remove the following 'keypad()' call to test this
4340              */
4341             keypad(current->wind, TRUE);
4342             current->do_keypad = HaveKeypad(current);
4343             current->do_scroll = HaveScroll(current);
4344             break;
4345
4346         case CTRL('N'): /* go to next window */
4347             if (current)
4348                 current = current->next;
4349             break;
4350
4351         case CTRL('P'): /* go to previous window */
4352             if (current)
4353                 current = current->last;
4354             break;
4355
4356         case CTRL('F'): /* scroll current window forward */
4357             if (current)
4358                 wscrl(frame_win(current), 1);
4359             break;
4360
4361         case CTRL('B'): /* scroll current window backwards */
4362             if (current)
4363                 wscrl(frame_win(current), -1);
4364             break;
4365
4366         case CTRL('K'): /* toggle keypad mode for current */
4367             if (current) {
4368                 current->do_keypad = !current->do_keypad;
4369                 keypad(current->wind, current->do_keypad);
4370             }
4371             break;
4372
4373         case CTRL('S'):
4374             if (current) {
4375                 current->do_scroll = !current->do_scroll;
4376                 scrollok(current->wind, current->do_scroll);
4377             }
4378             break;
4379
4380 #if HAVE_PUTWIN && HAVE_GETWIN
4381         case CTRL('W'): /* save and delete window */
4382             if ((current != 0) && (current == current->next)) {
4383                 transient(current, "Will not save/delete ONLY window");
4384                 break;
4385             } else if ((fp = fopen(DUMPFILE, "w")) == (FILE *) 0) {
4386                 transient(current, "Can't open screen dump file");
4387             } else {
4388                 (void) putwin(frame_win(current), fp);
4389                 (void) fclose(fp);
4390
4391                 current = delete_framed(current, TRUE);
4392             }
4393             break;
4394
4395         case CTRL('R'): /* restore window */
4396             if ((fp = fopen(DUMPFILE, "r")) == (FILE *) 0) {
4397                 transient(current, "Can't open screen dump file");
4398             } else {
4399                 if ((neww = typeCalloc(FRAME, 1)) != 0) {
4400
4401                     neww->next = current ? current->next : 0;
4402                     neww->last = current;
4403                     if (neww->last != 0)
4404                         neww->last->next = neww;
4405                     if (neww->next != 0)
4406                         neww->next->last = neww;
4407
4408                     neww->wind = getwin(fp);
4409
4410                     wrefresh(neww->wind);
4411                 } else {
4412                     failed("acs_and_scroll");
4413                 }
4414                 (void) fclose(fp);
4415             }
4416             break;
4417 #endif
4418
4419 #if HAVE_WRESIZE
4420         case CTRL('X'): /* resize window */
4421             if (current) {
4422                 pair *tmp, ul, lr;
4423                 int i, mx, my;
4424
4425                 move(0, 0);
4426                 clrtoeol();
4427                 addstr("Use arrows to move cursor, anything else to mark new corner");
4428                 refresh();
4429
4430                 getbegyx(current->wind, ul.y, ul.x);
4431
4432                 tmp = selectcell(ul.y, ul.x, LINES - BOTLINES - 2, COLS - 2);
4433                 if (tmp == (pair *) 0) {
4434                     beep();
4435                     break;
4436                 }
4437
4438                 getmaxyx(current->wind, lr.y, lr.x);
4439                 lr.y += (ul.y - 1);
4440                 lr.x += (ul.x - 1);
4441                 outerbox(ul, lr, FALSE);
4442                 wnoutrefresh(stdscr);
4443
4444                 /* strictly cosmetic hack for the test */
4445                 getmaxyx(current->wind, my, mx);
4446                 if (my > tmp->y - ul.y) {
4447                     getyx(current->wind, lr.y, lr.x);
4448                     wmove(current->wind, tmp->y - ul.y + 1, 0);
4449                     wclrtobot(current->wind);
4450                     wmove(current->wind, lr.y, lr.x);
4451                 }
4452                 if (mx > tmp->x - ul.x)
4453                     for (i = 0; i < my; i++) {
4454                         wmove(current->wind, i, tmp->x - ul.x + 1);
4455                         wclrtoeol(current->wind);
4456                     }
4457                 wnoutrefresh(current->wind);
4458
4459                 memcpy(&lr, tmp, sizeof(pair));
4460                 (void) wresize(current->wind, lr.y - ul.y + 0, lr.x - ul.x + 0);
4461
4462                 getbegyx(current->wind, ul.y, ul.x);
4463                 getmaxyx(current->wind, lr.y, lr.x);
4464                 lr.y += (ul.y - 1);
4465                 lr.x += (ul.x - 1);
4466                 outerbox(ul, lr, TRUE);
4467                 wnoutrefresh(stdscr);
4468
4469                 wnoutrefresh(current->wind);
4470                 move(0, 0);
4471                 clrtoeol();
4472                 doupdate();
4473             }
4474             break;
4475 #endif /* HAVE_WRESIZE */
4476
4477         case KEY_F(10): /* undocumented --- use this to test area clears */
4478             selectcell(0, 0, LINES - 1, COLS - 1);
4479             clrtobot();
4480             refresh();
4481             break;
4482
4483         case KEY_UP:
4484             newwin_move(current, -1, 0);
4485             break;
4486         case KEY_DOWN:
4487             newwin_move(current, 1, 0);
4488             break;
4489         case KEY_LEFT:
4490             newwin_move(current, 0, -1);
4491             break;
4492         case KEY_RIGHT:
4493             newwin_move(current, 0, 1);
4494             break;
4495
4496         case KEY_BACKSPACE:
4497             /* FALLTHROUGH */
4498         case KEY_DC:
4499             {
4500                 int y, x;
4501                 getyx(frame_win(current), y, x);
4502                 if (--x < 0) {
4503                     if (--y < 0)
4504                         break;
4505                     x = getmaxx(frame_win(current)) - 1;
4506                 }
4507                 (void) mvwdelch(frame_win(current), y, x);
4508             }
4509             break;
4510
4511         case '\r':
4512             c = '\n';
4513             /* FALLTHROUGH */
4514
4515         default:
4516             if (current)
4517                 waddch(current->wind, (chtype) c);
4518             else
4519                 beep();
4520             break;
4521         }
4522         newwin_report(current);
4523         usescr = frame_win(current);
4524         wrefresh(usescr);
4525     } while
4526         (!isQuit(c = wGetchar(usescr))
4527          && (c != ERR));
4528
4529   breakout:
4530     while (current != 0)
4531         current = delete_framed(current, FALSE);
4532
4533     scrollok(stdscr, TRUE);     /* reset to driver's default */
4534 #ifdef NCURSES_MOUSE_VERSION
4535     mousemask(0, (mmask_t *) 0);
4536 #endif
4537     noraw();
4538     erase();
4539     endwin();
4540 }
4541
4542 /****************************************************************************
4543  *
4544  * Panels tester
4545  *
4546  ****************************************************************************/
4547
4548 #if USE_LIBPANEL
4549 static int nap_msec = 1;
4550
4551 static NCURSES_CONST char *mod[] =
4552 {
4553     "test ",
4554     "TEST ",
4555     "(**) ",
4556     "*()* ",
4557     "<--> ",
4558     "LAST "
4559 };
4560
4561 /*+-------------------------------------------------------------------------
4562         wait_a_while(msec)
4563 --------------------------------------------------------------------------*/
4564 static void
4565 wait_a_while(int msec GCC_UNUSED)
4566 {
4567 #if HAVE_NAPMS
4568     if (nap_msec == 1)
4569         wGetchar(stdscr);
4570     else
4571         napms(nap_msec);
4572 #else
4573     if (nap_msec == 1)
4574         wGetchar(stdscr);
4575     else if (msec > 1000)
4576         sleep((unsigned) msec / 1000);
4577     else
4578         sleep(1);
4579 #endif
4580 }                               /* end of wait_a_while */
4581
4582 /*+-------------------------------------------------------------------------
4583         saywhat(text)
4584 --------------------------------------------------------------------------*/
4585 static void
4586 saywhat(NCURSES_CONST char *text)
4587 {
4588     wmove(stdscr, LINES - 1, 0);
4589     wclrtoeol(stdscr);
4590     if (text != 0 && *text != '\0') {
4591         waddstr(stdscr, text);
4592         waddstr(stdscr, "; ");
4593     }
4594     waddstr(stdscr, "press any key to continue");
4595 }                               /* end of saywhat */
4596
4597 /*+-------------------------------------------------------------------------
4598         mkpanel(rows,cols,tly,tlx) - alloc a win and panel and associate them
4599 --------------------------------------------------------------------------*/
4600 static PANEL *
4601 mkpanel(short color, int rows, int cols, int tly, int tlx)
4602 {
4603     WINDOW *win;
4604     PANEL *pan = 0;
4605
4606     if ((win = newwin(rows, cols, tly, tlx)) != 0) {
4607         if ((pan = new_panel(win)) == 0) {
4608             delwin(win);
4609         } else if (use_colors) {
4610             short fg = (short) ((color == COLOR_BLUE) ? COLOR_WHITE : COLOR_BLACK);
4611             short bg = color;
4612
4613             init_pair(color, fg, bg);
4614             wbkgdset(win, (attr_t) (COLOR_PAIR(color) | ' '));
4615         } else {
4616             wbkgdset(win, A_BOLD | ' ');
4617         }
4618     }
4619     return pan;
4620 }                               /* end of mkpanel */
4621
4622 /*+-------------------------------------------------------------------------
4623         rmpanel(pan)
4624 --------------------------------------------------------------------------*/
4625 static void
4626 rmpanel(PANEL * pan)
4627 {
4628     WINDOW *win = panel_window(pan);
4629     del_panel(pan);
4630     delwin(win);
4631 }                               /* end of rmpanel */
4632
4633 /*+-------------------------------------------------------------------------
4634         pflush()
4635 --------------------------------------------------------------------------*/
4636 static void
4637 pflush(void)
4638 {
4639     update_panels();
4640     doupdate();
4641 }                               /* end of pflush */
4642
4643 /*+-------------------------------------------------------------------------
4644         fill_panel(win)
4645 --------------------------------------------------------------------------*/
4646 static void
4647 init_panel(void)
4648 {
4649     register int y, x;
4650
4651     for (y = 0; y < LINES - 1; y++) {
4652         for (x = 0; x < COLS; x++)
4653             wprintw(stdscr, "%d", (y + x) % 10);
4654     }
4655 }
4656
4657 static void
4658 fill_panel(PANEL * pan)
4659 {
4660     WINDOW *win = panel_window(pan);
4661     const char *userptr = (const char *) panel_userptr(pan);
4662     int num = (userptr && *userptr) ? userptr[1] : '?';
4663     int y, x;
4664
4665     wmove(win, 1, 1);
4666     wprintw(win, "-pan%c-", num);
4667     wclrtoeol(win);
4668     box(win, 0, 0);
4669     for (y = 2; y < getmaxy(win) - 1; y++) {
4670         for (x = 1; x < getmaxx(win) - 1; x++) {
4671             wmove(win, y, x);
4672             waddch(win, UChar(num));
4673         }
4674     }
4675 }
4676
4677 #if USE_WIDEC_SUPPORT
4678 static void
4679 init_wide_panel(void)
4680 {
4681     int digit;
4682     cchar_t temp[10];
4683
4684     for (digit = 0; digit < 10; ++digit)
4685         make_fullwidth_digit(&temp[digit], digit);
4686
4687     do {
4688         int y, x;
4689         getyx(stdscr, y, x);
4690         digit = (y + x / 2) % 10;
4691     } while (add_wch(&temp[digit]) != ERR);
4692 }
4693
4694 static void
4695 fill_wide_panel(PANEL * pan)
4696 {
4697     WINDOW *win = panel_window(pan);
4698     const char *userptr = (const char *) panel_userptr(pan);
4699     int num = (userptr && *userptr) ? userptr[1] : '?';
4700     int y, x;
4701
4702     wmove(win, 1, 1);
4703     wprintw(win, "-pan%c-", num);
4704     wclrtoeol(win);
4705     box(win, 0, 0);
4706     for (y = 2; y < getmaxy(win) - 1; y++) {
4707         for (x = 1; x < getmaxx(win) - 1; x++) {
4708             wmove(win, y, x);
4709             waddch(win, UChar(num));
4710         }
4711     }
4712 }
4713 #endif
4714
4715 #define MAX_PANELS 5
4716
4717 static void
4718 canned_panel(PANEL * px[MAX_PANELS + 1], NCURSES_CONST char *cmd)
4719 {
4720     int which = cmd[1] - '0';
4721
4722     saywhat(cmd);
4723     switch (*cmd) {
4724     case 'h':
4725         hide_panel(px[which]);
4726         break;
4727     case 's':
4728         show_panel(px[which]);
4729         break;
4730     case 't':
4731         top_panel(px[which]);
4732         break;
4733     case 'b':
4734         bottom_panel(px[which]);
4735         break;
4736     case 'd':
4737         rmpanel(px[which]);
4738         break;
4739     }
4740     pflush();
4741     wait_a_while(nap_msec);
4742 }
4743
4744 static void
4745 demo_panels(void (*InitPanel) (void), void (*FillPanel) (PANEL *))
4746 {
4747     int count;
4748     int itmp;
4749     PANEL *px[MAX_PANELS + 1];
4750
4751     scrollok(stdscr, FALSE);    /* we don't want stdscr to scroll! */
4752     refresh();
4753
4754     InitPanel();
4755     for (count = 0; count < 5; count++) {
4756         px[1] = mkpanel(COLOR_RED,
4757                         LINES / 2 - 2,
4758                         COLS / 8 + 1,
4759                         0,
4760                         0);
4761         set_panel_userptr(px[1], (NCURSES_CONST void *) "p1");
4762
4763         px[2] = mkpanel(COLOR_GREEN,
4764                         LINES / 2 + 1,
4765                         COLS / 7,
4766                         LINES / 4,
4767                         COLS / 10);
4768         set_panel_userptr(px[2], (NCURSES_CONST void *) "p2");
4769
4770         px[3] = mkpanel(COLOR_YELLOW,
4771                         LINES / 4,
4772                         COLS / 10,
4773                         LINES / 2,
4774                         COLS / 9);
4775         set_panel_userptr(px[3], (NCURSES_CONST void *) "p3");
4776
4777         px[4] = mkpanel(COLOR_BLUE,
4778                         LINES / 2 - 2,
4779                         COLS / 8,
4780                         LINES / 2 - 2,
4781                         COLS / 3);
4782         set_panel_userptr(px[4], (NCURSES_CONST void *) "p4");
4783
4784         px[5] = mkpanel(COLOR_MAGENTA,
4785                         LINES / 2 - 2,
4786                         COLS / 8,
4787                         LINES / 2,
4788                         COLS / 2 - 2);
4789         set_panel_userptr(px[5], (NCURSES_CONST void *) "p5");
4790
4791         FillPanel(px[1]);
4792         FillPanel(px[2]);
4793         FillPanel(px[3]);
4794         FillPanel(px[4]);
4795         FillPanel(px[5]);
4796
4797         hide_panel(px[4]);
4798         hide_panel(px[5]);
4799         pflush();
4800         saywhat("");
4801         wait_a_while(nap_msec);
4802
4803         saywhat("h3 s1 s2 s4 s5");
4804         move_panel(px[1], 0, 0);
4805         hide_panel(px[3]);
4806         show_panel(px[1]);
4807         show_panel(px[2]);
4808         show_panel(px[4]);
4809         show_panel(px[5]);
4810         pflush();
4811         wait_a_while(nap_msec);
4812
4813         canned_panel(px, "s1");
4814         canned_panel(px, "s2");
4815
4816         saywhat("m2");
4817         move_panel(px[2], LINES / 3 + 1, COLS / 8);
4818         pflush();
4819         wait_a_while(nap_msec);
4820
4821         canned_panel(px, "s3");
4822
4823         saywhat("m3");
4824         move_panel(px[3], LINES / 4 + 1, COLS / 15);
4825         pflush();
4826         wait_a_while(nap_msec);
4827
4828         canned_panel(px, "b3");
4829         canned_panel(px, "s4");
4830         canned_panel(px, "s5");
4831         canned_panel(px, "t3");
4832         canned_panel(px, "t1");
4833         canned_panel(px, "t2");
4834         canned_panel(px, "t3");
4835         canned_panel(px, "t4");
4836
4837         for (itmp = 0; itmp < 6; itmp++) {
4838             WINDOW *w4 = panel_window(px[4]);
4839             WINDOW *w5 = panel_window(px[5]);
4840
4841             saywhat("m4");
4842             wmove(w4, LINES / 8, 1);
4843             waddstr(w4, mod[itmp]);
4844             move_panel(px[4], LINES / 6, itmp * (COLS / 8));
4845             wmove(w5, LINES / 6, 1);
4846             waddstr(w5, mod[itmp]);
4847             pflush();
4848             wait_a_while(nap_msec);
4849
4850             saywhat("m5");
4851             wmove(w4, LINES / 6, 1);
4852             waddstr(w4, mod[itmp]);
4853             move_panel(px[5], LINES / 3 - 1, (itmp * 10) + 6);
4854             wmove(w5, LINES / 8, 1);
4855             waddstr(w5, mod[itmp]);
4856             pflush();
4857             wait_a_while(nap_msec);
4858         }
4859
4860         saywhat("m4");
4861         move_panel(px[4], LINES / 6, itmp * (COLS / 8));
4862         pflush();
4863         wait_a_while(nap_msec);
4864
4865         canned_panel(px, "t5");
4866         canned_panel(px, "t2");
4867         canned_panel(px, "t1");
4868         canned_panel(px, "d2");
4869         canned_panel(px, "h3");
4870         canned_panel(px, "d1");
4871         canned_panel(px, "d4");
4872         canned_panel(px, "d5");
4873         canned_panel(px, "d3");
4874
4875         wait_a_while(nap_msec);
4876         if (nap_msec == 1)
4877             break;
4878         nap_msec = 100L;
4879     }
4880
4881     erase();
4882     endwin();
4883 }
4884 #endif /* USE_LIBPANEL */
4885
4886 /****************************************************************************
4887  *
4888  * Pad tester
4889  *
4890  ****************************************************************************/
4891
4892 #define GRIDSIZE        3
4893
4894 static bool pending_pan = FALSE;
4895 static bool show_panner_legend = TRUE;
4896
4897 static int
4898 panner_legend(int line)
4899 {
4900     static const char *const legend[] =
4901     {
4902         "Use arrow keys (or U,D,L,R) to pan, ESC to quit, ! to shell-out.",
4903         "Use +,- (or j,k) to grow/shrink the panner vertically.",
4904         "Use <,> (or h,l) to grow/shrink the panner horizontally.",
4905         "Number repeats.  Toggle legend:? filler:a timer:t scrollmark:s."
4906     };
4907     int n = ((int) SIZEOF(legend) - (LINES - line));
4908     if (n >= 0) {
4909         if (move(line, 0) != ERR) {
4910             if (show_panner_legend)
4911                 printw("%s", legend[n]);
4912             clrtoeol();
4913             return show_panner_legend;
4914         }
4915     }
4916     return FALSE;
4917 }
4918
4919 static void
4920 panner_h_cleanup(int from_y, int from_x, int to_x)
4921 {
4922     if (!panner_legend(from_y))
4923         do_h_line(from_y, from_x, ' ', to_x);
4924 }
4925
4926 static void
4927 panner_v_cleanup(int from_y, int from_x, int to_y)
4928 {
4929     if (!panner_legend(from_y))
4930         do_v_line(from_y, from_x, ' ', to_y);
4931 }
4932
4933 static void
4934 fill_pad(WINDOW *panpad, bool pan_lines)
4935 {
4936     int y, x;
4937     unsigned gridcount = 0;
4938
4939     wmove(panpad, 0, 0);
4940     for (y = 0; y < getmaxy(panpad); y++) {
4941         for (x = 0; x < getmaxx(panpad); x++) {
4942             if (y % GRIDSIZE == 0 && x % GRIDSIZE == 0) {
4943                 if (y == 0 && x == 0)
4944                     waddch(panpad, pan_lines ? ACS_ULCORNER : '+');
4945                 else if (y == 0)
4946                     waddch(panpad, pan_lines ? ACS_TTEE : '+');
4947                 else if (y == 0 || x == 0)
4948                     waddch(panpad, pan_lines ? ACS_LTEE : '+');
4949                 else
4950                     waddch(panpad, (chtype) ((pan_lines ? 'a' : 'A') +
4951                                              (int) (gridcount++ % 26)));
4952             } else if (y % GRIDSIZE == 0)
4953                 waddch(panpad, pan_lines ? ACS_HLINE : '-');
4954             else if (x % GRIDSIZE == 0)
4955                 waddch(panpad, pan_lines ? ACS_VLINE : '|');
4956             else
4957                 waddch(panpad, ' ');
4958         }
4959     }
4960 }
4961
4962 static void
4963 panner(WINDOW *pad,
4964        int top_x, int top_y, int porty, int portx,
4965        int (*pgetc) (WINDOW *))
4966 {
4967 #if HAVE_GETTIMEOFDAY
4968     struct timeval before, after;
4969     bool timing = TRUE;
4970 #endif
4971     bool pan_lines = FALSE;
4972     bool scrollers = TRUE;
4973     int basex = 0;
4974     int basey = 0;
4975     int pxmax, pymax, lowend, highend, c;
4976
4977     getmaxyx(pad, pymax, pxmax);
4978     scrollok(stdscr, FALSE);    /* we don't want stdscr to scroll! */
4979
4980     c = KEY_REFRESH;
4981     do {
4982 #ifdef NCURSES_VERSION
4983         /*
4984          * During shell-out, the user may have resized the window.  Adjust
4985          * the port size of the pad to accommodate this.  Ncurses automatically
4986          * resizes all of the normal windows to fit on the new screen.
4987          */
4988         if (top_x > COLS)
4989             top_x = COLS;
4990         if (portx > COLS)
4991             portx = COLS;
4992         if (top_y > LINES)
4993             top_y = LINES;
4994         if (porty > LINES)
4995             porty = LINES;
4996 #endif
4997         switch (c) {
4998         case KEY_REFRESH:
4999             erase();
5000
5001             /* FALLTHRU */
5002         case '?':
5003             if (c == '?')
5004                 show_panner_legend = !show_panner_legend;
5005             panner_legend(LINES - 4);
5006             panner_legend(LINES - 3);
5007             panner_legend(LINES - 2);
5008             panner_legend(LINES - 1);
5009             break;
5010         case 'a':
5011             pan_lines = !pan_lines;
5012             fill_pad(pad, pan_lines);
5013             pending_pan = FALSE;
5014             break;
5015
5016 #if HAVE_GETTIMEOFDAY
5017         case 't':
5018             timing = !timing;
5019             if (!timing)
5020                 panner_legend(LINES - 1);
5021             break;
5022 #endif
5023         case 's':
5024             scrollers = !scrollers;
5025             break;
5026
5027             /* Move the top-left corner of the pad, keeping the bottom-right
5028              * corner fixed.
5029              */
5030         case 'h':               /* increase-columns: move left edge to left */
5031             if (top_x <= 0)
5032                 beep();
5033             else {
5034                 panner_v_cleanup(top_y, top_x, porty);
5035                 top_x--;
5036             }
5037             break;
5038
5039         case 'j':               /* decrease-lines: move top-edge down */
5040             if (top_y >= porty)
5041                 beep();
5042             else {
5043                 panner_h_cleanup(top_y - 1, top_x - (top_x > 0), portx);
5044                 top_y++;
5045             }
5046             break;
5047
5048         case 'k':               /* increase-lines: move top-edge up */
5049             if (top_y <= 0)
5050                 beep();
5051             else {
5052                 top_y--;
5053                 panner_h_cleanup(top_y, top_x, portx);
5054             }
5055             break;
5056
5057         case 'l':               /* decrease-columns: move left-edge to right */
5058             if (top_x >= portx)
5059                 beep();
5060             else {
5061                 panner_v_cleanup(top_y - (top_y > 0), top_x - 1, porty);
5062                 top_x++;
5063             }
5064             break;
5065
5066             /* Move the bottom-right corner of the pad, keeping the top-left
5067              * corner fixed.
5068              */
5069         case KEY_IC:            /* increase-columns: move right-edge to right */
5070             if (portx >= pxmax || portx >= COLS)
5071                 beep();
5072             else {
5073                 panner_v_cleanup(top_y - (top_y > 0), portx - 1, porty);
5074                 ++portx;
5075             }
5076             break;
5077
5078         case KEY_IL:            /* increase-lines: move bottom-edge down */
5079             if (porty >= pymax || porty >= LINES)
5080                 beep();
5081             else {
5082                 panner_h_cleanup(porty - 1, top_x - (top_x > 0), portx);
5083                 ++porty;
5084             }
5085             break;
5086
5087         case KEY_DC:            /* decrease-columns: move bottom edge up */
5088             if (portx <= top_x)
5089                 beep();
5090             else {
5091                 portx--;
5092                 panner_v_cleanup(top_y - (top_y > 0), portx, porty);
5093             }
5094             break;
5095
5096         case KEY_DL:            /* decrease-lines */
5097             if (porty <= top_y)
5098                 beep();
5099             else {
5100                 porty--;
5101                 panner_h_cleanup(porty, top_x - (top_x > 0), portx);
5102             }
5103             break;
5104
5105         case KEY_LEFT:          /* pan leftwards */
5106             if (basex > 0)
5107                 basex--;
5108             else
5109                 beep();
5110             break;
5111
5112         case KEY_RIGHT: /* pan rightwards */
5113             if (basex + portx - (pymax > porty) < pxmax)
5114                 basex++;
5115             else
5116                 beep();
5117             break;
5118
5119         case KEY_UP:            /* pan upwards */
5120             if (basey > 0)
5121                 basey--;
5122             else
5123                 beep();
5124             break;
5125
5126         case KEY_DOWN:          /* pan downwards */
5127             if (basey + porty - (pxmax > portx) < pymax)
5128                 basey++;
5129             else
5130                 beep();
5131             break;
5132
5133         case 'H':
5134         case KEY_HOME:
5135         case KEY_FIND:
5136             basey = 0;
5137             break;
5138
5139         case 'E':
5140         case KEY_END:
5141         case KEY_SELECT:
5142             basey = pymax - porty;
5143             if (basey < 0)
5144                 basey = 0;
5145             break;
5146
5147         default:
5148             beep();
5149             break;
5150         }
5151
5152         MvAddCh(top_y - 1, top_x - 1, ACS_ULCORNER);
5153         do_v_line(top_y, top_x - 1, ACS_VLINE, porty);
5154         do_h_line(top_y - 1, top_x, ACS_HLINE, portx);
5155
5156         if (scrollers && (pxmax > portx - 1)) {
5157             int length = (portx - top_x - 1);
5158             float ratio = ((float) length) / ((float) pxmax);
5159
5160             lowend = (int) ((float) top_x + ((float) basex * ratio));
5161             highend = (int) ((float) top_x + ((float) (basex + length) * ratio));
5162
5163             do_h_line(porty - 1, top_x, ACS_HLINE, lowend);
5164             if (highend < portx) {
5165                 attron(A_REVERSE);
5166                 do_h_line(porty - 1, lowend, ' ', highend + 1);
5167                 attroff(A_REVERSE);
5168                 do_h_line(porty - 1, highend + 1, ACS_HLINE, portx);
5169             }
5170         } else
5171             do_h_line(porty - 1, top_x, ACS_HLINE, portx);
5172
5173         if (scrollers && (pymax > porty - 1)) {
5174             int length = (porty - top_y - 1);
5175             float ratio = ((float) length) / ((float) pymax);
5176
5177             lowend = (int) ((float) top_y + ((float) basey * ratio));
5178             highend = (int) ((float) top_y + ((float) (basey + length) * ratio));
5179
5180             do_v_line(top_y, portx - 1, ACS_VLINE, lowend);
5181             if (highend < porty) {
5182                 attron(A_REVERSE);
5183                 do_v_line(lowend, portx - 1, ' ', highend + 1);
5184                 attroff(A_REVERSE);
5185                 do_v_line(highend + 1, portx - 1, ACS_VLINE, porty);
5186             }
5187         } else
5188             do_v_line(top_y, portx - 1, ACS_VLINE, porty);
5189
5190         MvAddCh(top_y - 1, portx - 1, ACS_URCORNER);
5191         MvAddCh(porty - 1, top_x - 1, ACS_LLCORNER);
5192         MvAddCh(porty - 1, portx - 1, ACS_LRCORNER);
5193
5194         if (!pending_pan) {
5195 #if HAVE_GETTIMEOFDAY
5196             gettimeofday(&before, 0);
5197 #endif
5198             wnoutrefresh(stdscr);
5199
5200             pnoutrefresh(pad,
5201                          basey, basex,
5202                          top_y, top_x,
5203                          porty - (pxmax > portx) - 1,
5204                          portx - (pymax > porty) - 1);
5205
5206             doupdate();
5207 #if HAVE_GETTIMEOFDAY
5208 #define TIMEVAL2S(data) ((double) data.tv_sec + ((double) data.tv_usec / 1.0e6))
5209             if (timing) {
5210                 double elapsed;
5211                 gettimeofday(&after, 0);
5212                 elapsed = (TIMEVAL2S(after) - TIMEVAL2S(before));
5213                 move(LINES - 1, COLS - 12);
5214                 printw("Secs: %2.03f", elapsed);
5215                 refresh();
5216             }
5217 #endif
5218         }
5219
5220     } while
5221         ((c = pgetc(pad)) != KEY_EXIT);
5222
5223     scrollok(stdscr, TRUE);     /* reset to driver's default */
5224 }
5225
5226 static int
5227 padgetch(WINDOW *win)
5228 {
5229     static int count;
5230     static int last;
5231     int c;
5232
5233     if ((pending_pan = (count > 0)) != FALSE) {
5234         count--;
5235         pending_pan = (count != 0);
5236     } else {
5237         for (;;) {
5238             switch (c = wGetchar(win)) {
5239             case '!':
5240                 ShellOut(FALSE);
5241                 /* FALLTHRU */
5242             case CTRL('r'):
5243                 endwin();
5244                 refresh();
5245                 c = KEY_REFRESH;
5246                 break;
5247             case CTRL('l'):
5248                 c = KEY_REFRESH;
5249                 break;
5250             case 'U':
5251                 c = KEY_UP;
5252                 break;
5253             case 'D':
5254                 c = KEY_DOWN;
5255                 break;
5256             case 'R':
5257                 c = KEY_RIGHT;
5258                 break;
5259             case 'L':
5260                 c = KEY_LEFT;
5261                 break;
5262             case '+':
5263                 c = KEY_IL;
5264                 break;
5265             case '-':
5266                 c = KEY_DL;
5267                 break;
5268             case '>':
5269                 c = KEY_IC;
5270                 break;
5271             case '<':
5272                 c = KEY_DC;
5273                 break;
5274             case ERR:           /* FALLTHRU */
5275             case case_QUIT:
5276                 count = 0;
5277                 c = KEY_EXIT;
5278                 break;
5279             default:
5280                 if (c >= '0' && c <= '9') {
5281                     count = count * 10 + (c - '0');
5282                     continue;
5283                 }
5284                 break;
5285             }
5286             last = c;
5287             break;
5288         }
5289         if (count > 0)
5290             count--;
5291     }
5292     return (last);
5293 }
5294
5295 #define PAD_HIGH 200
5296 #define PAD_WIDE 200
5297
5298 static void
5299 demo_pad(void)
5300 /* Demonstrate pads. */
5301 {
5302     WINDOW *panpad = newpad(PAD_HIGH, PAD_WIDE);
5303
5304     if (panpad == 0) {
5305         Cannot("cannot create requested pad");
5306         return;
5307     }
5308
5309     fill_pad(panpad, FALSE);
5310
5311     panner_legend(LINES - 4);
5312     panner_legend(LINES - 3);
5313     panner_legend(LINES - 2);
5314     panner_legend(LINES - 1);
5315
5316     keypad(panpad, TRUE);
5317
5318     /* Make the pad (initially) narrow enough that a trace file won't wrap.
5319      * We'll still be able to widen it during a test, since that's required
5320      * for testing boundaries.
5321      */
5322     panner(panpad, 2, 2, LINES - 5, COLS - 15, padgetch);
5323
5324     delwin(panpad);
5325     endwin();
5326     erase();
5327 }
5328
5329 /****************************************************************************
5330  *
5331  * Tests from John Burnell's PDCurses tester
5332  *
5333  ****************************************************************************/
5334
5335 static void
5336 Continue(WINDOW *win)
5337 {
5338     noecho();
5339     wmove(win, 10, 1);
5340     MvWAddStr(win, 10, 1, " Press any key to continue");
5341     wrefresh(win);
5342     wGetchar(win);
5343 }
5344
5345 static void
5346 flushinp_test(WINDOW *win)
5347 /* Input test, adapted from John Burnell's PDCurses tester */
5348 {
5349     int w, h, bx, by, sw, sh, i;
5350
5351     WINDOW *subWin;
5352     wclear(win);
5353
5354     getmaxyx(win, h, w);
5355     getbegyx(win, by, bx);
5356     sw = w / 3;
5357     sh = h / 3;
5358     if ((subWin = subwin(win, sh, sw, by + h - sh - 2, bx + w - sw - 2)) == 0)
5359         return;
5360
5361 #ifdef A_COLOR
5362     if (use_colors) {
5363         init_pair(2, COLOR_CYAN, COLOR_BLUE);
5364         wbkgd(subWin, (chtype) (COLOR_PAIR(2) | ' '));
5365     }
5366 #endif
5367     (void) wattrset(subWin, A_BOLD);
5368     box(subWin, ACS_VLINE, ACS_HLINE);
5369     MvWAddStr(subWin, 2, 1, "This is a subwindow");
5370     wrefresh(win);
5371
5372     /*
5373      * This used to set 'nocbreak()'.  However, Alexander Lukyanov says that
5374      * it only happened to "work" on SVr4 because that implementation does not
5375      * emulate nocbreak+noecho mode, whereas ncurses does.  To get the desired
5376      * test behavior, we're using 'cbreak()', which will allow a single
5377      * character to return without needing a newline. - T.Dickey 1997/10/11.
5378      */
5379     cbreak();
5380     MvWAddStr(win, 0, 1, "This is a test of the flushinp() call.");
5381
5382     MvWAddStr(win, 2, 1, "Type random keys for 5 seconds.");
5383     MvWAddStr(win, 3, 1,
5384               "These should be discarded (not echoed) after the subwindow goes away.");
5385     wrefresh(win);
5386
5387     for (i = 0; i < 5; i++) {
5388         MvWPrintw(subWin, 1, 1, "Time = %d", i);
5389         wrefresh(subWin);
5390         napms(1000);
5391         flushinp();
5392     }
5393
5394     delwin(subWin);
5395     werase(win);
5396     flash();
5397     wrefresh(win);
5398     napms(1000);
5399
5400     MvWAddStr(win, 2, 1,
5401               "If you were still typing when the window timer expired,");
5402     MvWAddStr(win, 3, 1,
5403               "or else you typed nothing at all while it was running,");
5404     MvWAddStr(win, 4, 1,
5405               "test was invalid.  You'll see garbage or nothing at all. ");
5406     MvWAddStr(win, 6, 1, "Press a key");
5407     wmove(win, 9, 10);
5408     wrefresh(win);
5409     echo();
5410     wGetchar(win);
5411     flushinp();
5412     MvWAddStr(win, 12, 0,
5413               "If you see any key other than what you typed, flushinp() is broken.");
5414     Continue(win);
5415
5416     wmove(win, 9, 10);
5417     wdelch(win);
5418     wrefresh(win);
5419     wmove(win, 12, 0);
5420     clrtoeol();
5421     waddstr(win,
5422             "What you typed should now have been deleted; if not, wdelch() failed.");
5423     Continue(win);
5424
5425     cbreak();
5426 }
5427
5428 /****************************************************************************
5429  *
5430  * Menu test
5431  *
5432  ****************************************************************************/
5433
5434 #if USE_LIBMENU
5435
5436 #define MENU_Y  8
5437 #define MENU_X  8
5438
5439 static int
5440 menu_virtualize(int c)
5441 {
5442     if (c == '\n' || c == KEY_EXIT)
5443         return (MAX_COMMAND + 1);
5444     else if (c == 'u')
5445         return (REQ_SCR_ULINE);
5446     else if (c == 'd')
5447         return (REQ_SCR_DLINE);
5448     else if (c == 'b' || c == KEY_NPAGE)
5449         return (REQ_SCR_UPAGE);
5450     else if (c == 'f' || c == KEY_PPAGE)
5451         return (REQ_SCR_DPAGE);
5452     else if (c == 'n' || c == KEY_DOWN)
5453         return (REQ_NEXT_ITEM);
5454     else if (c == 'p' || c == KEY_UP)
5455         return (REQ_PREV_ITEM);
5456     else if (c == ' ')
5457         return (REQ_TOGGLE_ITEM);
5458     else {
5459         if (c != KEY_MOUSE)
5460             beep();
5461         return (c);
5462     }
5463 }
5464
5465 static CONST_MENUS char *animals[] =
5466 {
5467     "Lions",
5468     "Tigers",
5469     "Bears",
5470     "(Oh my!)",
5471     "Newts",
5472     "Platypi",
5473     "Lemurs",
5474     "(Oh really?!)",
5475     "Leopards",
5476     "Panthers",
5477     "Pumas",
5478     "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
5479     "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs, Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
5480     (char *) 0
5481 };
5482
5483 static void
5484 menu_test(void)
5485 {
5486     MENU *m;
5487     ITEM *items[SIZEOF(animals)];
5488     ITEM **ip = items;
5489     CONST_MENUS char **ap;
5490     int mrows, mcols, c;
5491     WINDOW *menuwin;
5492
5493 #ifdef NCURSES_MOUSE_VERSION
5494     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
5495 #endif
5496     MvAddStr(0, 0, "This is the menu test:");
5497     MvAddStr(2, 0, "  Use up and down arrow to move the select bar.");
5498     MvAddStr(3, 0, "  'n' and 'p' act like arrows.");
5499     MvAddStr(4, 0,
5500              "  'b' and 'f' scroll up/down (page), 'u' and 'd' (line).");
5501     MvAddStr(5, 0, "  Press return to exit.");
5502     refresh();
5503
5504     for (ap = animals; *ap; ap++) {
5505         if ((*ip = new_item(*ap, "")) != 0)
5506             ++ip;
5507     }
5508     *ip = (ITEM *) 0;
5509
5510     m = new_menu(items);
5511
5512     set_menu_format(m, (SIZEOF(animals) + 1) / 2, 1);
5513     scale_menu(m, &mrows, &mcols);
5514
5515     menuwin = newwin(mrows + 2, mcols + 2, MENU_Y, MENU_X);
5516     set_menu_win(m, menuwin);
5517     keypad(menuwin, TRUE);
5518     box(menuwin, 0, 0);
5519
5520     set_menu_sub(m, derwin(menuwin, mrows, mcols, 1, 1));
5521
5522     post_menu(m);
5523
5524     while ((c = menu_driver(m, menu_virtualize(wGetchar(menuwin)))) != E_UNKNOWN_COMMAND) {
5525         if (c == E_NOT_POSTED)
5526             break;
5527         if (c == E_REQUEST_DENIED)
5528             beep();
5529         continue;
5530     }
5531
5532     MvPrintw(LINES - 2, 0,
5533              "You chose: %s\n", item_name(current_item(m)));
5534     (void) addstr("Press any key to continue...");
5535     wGetchar(stdscr);
5536
5537     unpost_menu(m);
5538     delwin(menuwin);
5539
5540     free_menu(m);
5541     for (ip = items; *ip; ip++)
5542         free_item(*ip);
5543 #ifdef NCURSES_MOUSE_VERSION
5544     mousemask(0, (mmask_t *) 0);
5545 #endif
5546 }
5547
5548 #ifdef TRACE
5549 #define T_TBL(name) { #name, name }
5550 static struct {
5551     const char *name;
5552     unsigned mask;
5553 } t_tbl[] = {
5554
5555     T_TBL(TRACE_DISABLE),
5556         T_TBL(TRACE_TIMES),
5557         T_TBL(TRACE_TPUTS),
5558         T_TBL(TRACE_UPDATE),
5559         T_TBL(TRACE_MOVE),
5560         T_TBL(TRACE_CHARPUT),
5561         T_TBL(TRACE_ORDINARY),
5562         T_TBL(TRACE_CALLS),
5563         T_TBL(TRACE_VIRTPUT),
5564         T_TBL(TRACE_IEVENT),
5565         T_TBL(TRACE_BITS),
5566         T_TBL(TRACE_ICALLS),
5567         T_TBL(TRACE_CCALLS),
5568         T_TBL(TRACE_DATABASE),
5569         T_TBL(TRACE_ATTRS),
5570         T_TBL(TRACE_MAXIMUM),
5571     {
5572         (char *) 0, 0
5573     }
5574 };
5575
5576 static char *
5577 tracetrace(unsigned tlevel)
5578 {
5579     static char *buf;
5580     int n;
5581
5582     if (buf == 0) {
5583         size_t need = 12;
5584         for (n = 0; t_tbl[n].name != 0; n++)
5585             need += strlen(t_tbl[n].name) + 2;
5586         buf = typeMalloc(char, need);
5587         if (!buf)
5588             failed("tracetrace");
5589     }
5590     sprintf(buf, "0x%02x = {", tlevel);
5591     if (tlevel == 0) {
5592         sprintf(buf + strlen(buf), "%s, ", t_tbl[0].name);
5593     } else {
5594         for (n = 1; t_tbl[n].name != 0; n++)
5595             if ((tlevel & t_tbl[n].mask) == t_tbl[n].mask) {
5596                 strcat(buf, t_tbl[n].name);
5597                 strcat(buf, ", ");
5598             }
5599     }
5600     if (buf[strlen(buf) - 2] == ',')
5601         buf[strlen(buf) - 2] = '\0';
5602     return (strcat(buf, "}"));
5603 }
5604
5605 /* fake a dynamically reconfigurable menu using the 0th entry to deselect
5606  * the others
5607  */
5608 static int
5609 run_trace_menu(MENU * m)
5610 {
5611     ITEM **items;
5612     ITEM *i, **p;
5613
5614     for (;;) {
5615         bool changed = FALSE;
5616         switch (menu_driver(m, menu_virtualize(wGetchar(menu_win(m))))) {
5617         case E_UNKNOWN_COMMAND:
5618             return FALSE;
5619         default:
5620             items = menu_items(m);
5621             i = current_item(m);
5622             if (i == items[0]) {
5623                 if (item_value(i)) {
5624                     for (p = items + 1; *p != 0; p++)
5625                         if (item_value(*p)) {
5626                             set_item_value(*p, FALSE);
5627                             changed = TRUE;
5628                         }
5629                 }
5630             } else {
5631                 for (p = items + 1; *p != 0; p++)
5632                     if (item_value(*p)) {
5633                         set_item_value(items[0], FALSE);
5634                         changed = TRUE;
5635                         break;
5636                     }
5637             }
5638             if (!changed)
5639                 return TRUE;
5640         }
5641     }
5642 }
5643
5644 static void
5645 trace_set(void)
5646 /* interactively set the trace level */
5647 {
5648     MENU *m;
5649     ITEM *items[SIZEOF(t_tbl)];
5650     ITEM **ip = items;
5651     int mrows, mcols;
5652     unsigned newtrace;
5653     int n;
5654     WINDOW *menuwin;
5655
5656     MvAddStr(0, 0, "Interactively set trace level:");
5657     MvAddStr(2, 0, "  Press space bar to toggle a selection.");
5658     MvAddStr(3, 0, "  Use up and down arrow to move the select bar.");
5659     MvAddStr(4, 0, "  Press return to set the trace level.");
5660     MvPrintw(6, 0, "(Current trace level is %s)", tracetrace(_nc_tracing));
5661
5662     refresh();
5663
5664     for (n = 0; t_tbl[n].name != 0; n++) {
5665         if ((*ip = new_item(t_tbl[n].name, "")) != 0) {
5666             ++ip;
5667         }
5668     }
5669     *ip = (ITEM *) 0;
5670
5671     m = new_menu(items);
5672
5673     set_menu_format(m, 0, 2);
5674     scale_menu(m, &mrows, &mcols);
5675
5676     menu_opts_off(m, O_ONEVALUE);
5677     menuwin = newwin(mrows + 2, mcols + 2, MENU_Y, MENU_X);
5678     set_menu_win(m, menuwin);
5679     keypad(menuwin, TRUE);
5680     box(menuwin, 0, 0);
5681
5682     set_menu_sub(m, derwin(menuwin, mrows, mcols, 1, 1));
5683
5684     post_menu(m);
5685
5686     for (ip = menu_items(m); *ip; ip++) {
5687         unsigned mask = t_tbl[item_index(*ip)].mask;
5688         if (mask == 0)
5689             set_item_value(*ip, _nc_tracing == 0);
5690         else if ((mask & _nc_tracing) == mask)
5691             set_item_value(*ip, TRUE);
5692     }
5693
5694     while (run_trace_menu(m))
5695         continue;
5696
5697     newtrace = 0;
5698     for (ip = menu_items(m); *ip; ip++)
5699         if (item_value(*ip))
5700             newtrace |= t_tbl[item_index(*ip)].mask;
5701     trace(newtrace);
5702     Trace(("trace level interactively set to %s", tracetrace(_nc_tracing)));
5703
5704     MvPrintw(LINES - 2, 0,
5705              "Trace level is %s\n", tracetrace(_nc_tracing));
5706     (void) addstr("Press any key to continue...");
5707     wGetchar(stdscr);
5708
5709     unpost_menu(m);
5710     delwin(menuwin);
5711
5712     free_menu(m);
5713     for (ip = items; *ip; ip++)
5714         free_item(*ip);
5715 }
5716 #endif /* TRACE */
5717 #endif /* USE_LIBMENU */
5718
5719 /****************************************************************************
5720  *
5721  * Forms test
5722  *
5723  ****************************************************************************/
5724 #if USE_LIBFORM
5725 static FIELD *
5726 make_label(int frow, int fcol, NCURSES_CONST char *label)
5727 {
5728     FIELD *f = new_field(1, (int) strlen(label), frow, fcol, 0, 0);
5729
5730     if (f) {
5731         set_field_buffer(f, 0, label);
5732         set_field_opts(f, (int) ((unsigned) field_opts(f) & ~O_ACTIVE));
5733     }
5734     return (f);
5735 }
5736
5737 static FIELD *
5738 make_field(int frow, int fcol, int rows, int cols, bool secure)
5739 {
5740     FIELD *f = new_field(rows, cols, frow, fcol, 0, secure ? 1 : 0);
5741
5742     if (f) {
5743         set_field_back(f, A_UNDERLINE);
5744         set_field_userptr(f, (void *) 0);
5745     }
5746     return (f);
5747 }
5748
5749 static void
5750 display_form(FORM * f)
5751 {
5752     WINDOW *w;
5753     int rows, cols;
5754
5755     scale_form(f, &rows, &cols);
5756
5757     if ((w = newwin(rows + 2, cols + 4, 0, 0)) != (WINDOW *) 0) {
5758         set_form_win(f, w);
5759         set_form_sub(f, derwin(w, rows, cols, 1, 2));
5760         box(w, 0, 0);
5761         keypad(w, TRUE);
5762         if (post_form(f) != E_OK)
5763             wrefresh(w);
5764     }
5765 }
5766
5767 static void
5768 erase_form(FORM * f)
5769 {
5770     WINDOW *w = form_win(f);
5771     WINDOW *s = form_sub(f);
5772
5773     unpost_form(f);
5774     werase(w);
5775     wrefresh(w);
5776     delwin(s);
5777     delwin(w);
5778 }
5779
5780 static int
5781 edit_secure(FIELD * me, int c)
5782 {
5783     int rows, cols, frow, fcol, nrow, nbuf;
5784
5785     if (field_info(me, &rows, &cols, &frow, &fcol, &nrow, &nbuf) == E_OK
5786         && nbuf > 0) {
5787         char *source = field_buffer(me, 1);
5788         char temp[80];
5789         long len;
5790
5791         strcpy(temp, source ? source : "");
5792         len = (long) (char *) field_userptr(me);
5793         if (c <= KEY_MAX) {
5794             if (isgraph(c) && (len + 1) < (int) sizeof(temp)) {
5795                 temp[len++] = (char) c;
5796                 temp[len] = 0;
5797                 set_field_buffer(me, 1, temp);
5798                 c = '*';
5799             } else {
5800                 c = 0;
5801             }
5802         } else {
5803             switch (c) {
5804             case REQ_BEG_FIELD:
5805             case REQ_CLR_EOF:
5806             case REQ_CLR_EOL:
5807             case REQ_DEL_LINE:
5808             case REQ_DEL_WORD:
5809             case REQ_DOWN_CHAR:
5810             case REQ_END_FIELD:
5811             case REQ_INS_CHAR:
5812             case REQ_INS_LINE:
5813             case REQ_LEFT_CHAR:
5814             case REQ_NEW_LINE:
5815             case REQ_NEXT_WORD:
5816             case REQ_PREV_WORD:
5817             case REQ_RIGHT_CHAR:
5818             case REQ_UP_CHAR:
5819                 c = 0;          /* we don't want to do inline editing */
5820                 break;
5821             case REQ_CLR_FIELD:
5822                 if (len) {
5823                     temp[0] = 0;
5824                     set_field_buffer(me, 1, temp);
5825                 }
5826                 break;
5827             case REQ_DEL_CHAR:
5828             case REQ_DEL_PREV:
5829                 if (len) {
5830                     temp[--len] = 0;
5831                     set_field_buffer(me, 1, temp);
5832                 }
5833                 break;
5834             }
5835         }
5836         set_field_userptr(me, (void *) len);
5837     }
5838     return c;
5839 }
5840
5841 static int
5842 form_virtualize(FORM * f, WINDOW *w)
5843 {
5844     /* *INDENT-OFF* */
5845     static const struct {
5846         int code;
5847         int result;
5848     } lookup[] = {
5849         { CTRL('A'),    REQ_NEXT_CHOICE },
5850         { CTRL('B'),    REQ_PREV_WORD },
5851         { CTRL('C'),    REQ_CLR_EOL },
5852         { CTRL('D'),    REQ_DOWN_FIELD },
5853         { CTRL('E'),    REQ_END_FIELD },
5854         { CTRL('F'),    REQ_NEXT_PAGE },
5855         { CTRL('G'),    REQ_DEL_WORD },
5856         { CTRL('H'),    REQ_DEL_PREV },
5857         { CTRL('I'),    REQ_INS_CHAR },
5858         { CTRL('K'),    REQ_CLR_EOF },
5859         { CTRL('L'),    REQ_LEFT_FIELD },
5860         { CTRL('M'),    REQ_NEW_LINE },
5861         { CTRL('N'),    REQ_NEXT_FIELD },
5862         { CTRL('O'),    REQ_INS_LINE },
5863         { CTRL('P'),    REQ_PREV_FIELD },
5864         { CTRL('R'),    REQ_RIGHT_FIELD },
5865         { CTRL('S'),    REQ_BEG_FIELD },
5866         { CTRL('U'),    REQ_UP_FIELD },
5867         { CTRL('V'),    REQ_DEL_CHAR },
5868         { CTRL('W'),    REQ_NEXT_WORD },
5869         { CTRL('X'),    REQ_CLR_FIELD },
5870         { CTRL('Y'),    REQ_DEL_LINE },
5871         { CTRL('Z'),    REQ_PREV_CHOICE },
5872         { ESCAPE,       MAX_FORM_COMMAND + 1 },
5873         { KEY_BACKSPACE, REQ_DEL_PREV },
5874         { KEY_DOWN,     REQ_DOWN_CHAR },
5875         { KEY_END,      REQ_LAST_FIELD },
5876         { KEY_HOME,     REQ_FIRST_FIELD },
5877         { KEY_LEFT,     REQ_LEFT_CHAR },
5878         { KEY_LL,       REQ_LAST_FIELD },
5879         { KEY_NEXT,     REQ_NEXT_FIELD },
5880         { KEY_NPAGE,    REQ_NEXT_PAGE },
5881         { KEY_PPAGE,    REQ_PREV_PAGE },
5882         { KEY_PREVIOUS, REQ_PREV_FIELD },
5883         { KEY_RIGHT,    REQ_RIGHT_CHAR },
5884         { KEY_UP,       REQ_UP_CHAR },
5885         { QUIT,         MAX_FORM_COMMAND + 1 }
5886     };
5887     /* *INDENT-ON* */
5888
5889     static int mode = REQ_INS_MODE;
5890     int c = wGetchar(w);
5891     unsigned n;
5892     FIELD *me = current_field(f);
5893     bool current = TRUE;
5894
5895     if (c == CTRL(']')) {
5896         if (mode == REQ_INS_MODE) {
5897             mode = REQ_OVL_MODE;
5898         } else {
5899             mode = REQ_INS_MODE;
5900         }
5901         c = mode;
5902     } else {
5903         for (n = 0; n < SIZEOF(lookup); n++) {
5904             if (lookup[n].code == c) {
5905                 c = lookup[n].result;
5906                 break;
5907             }
5908         }
5909     }
5910     MvPrintw(0, COLS - 6, "(%s)", mode == REQ_INS_MODE ? "INS" : "OVL");
5911
5912     /*
5913      * Force the field that the user is typing into to be in reverse video,
5914      * while the other fields are shown underlined.
5915      */
5916     switch (c) {
5917     case REQ_BEG_FIELD:
5918     case REQ_CLR_EOF:
5919     case REQ_CLR_EOL:
5920     case REQ_CLR_FIELD:
5921     case REQ_DEL_CHAR:
5922     case REQ_DEL_LINE:
5923     case REQ_DEL_PREV:
5924     case REQ_DEL_WORD:
5925     case REQ_END_FIELD:
5926     case REQ_INS_CHAR:
5927     case REQ_INS_LINE:
5928     case REQ_LEFT_CHAR:
5929     case REQ_LEFT_FIELD:
5930     case REQ_NEXT_WORD:
5931     case REQ_RIGHT_CHAR:
5932         current = TRUE;
5933         break;
5934     default:
5935         current = (c < KEY_MAX);
5936         break;
5937     }
5938     if (current) {
5939         c = edit_secure(me, c);
5940         set_field_back(me, A_REVERSE);
5941     } else {
5942         c = edit_secure(me, c);
5943         set_field_back(me, A_UNDERLINE);
5944     }
5945     return c;
5946 }
5947
5948 static int
5949 my_form_driver(FORM * form, int c)
5950 {
5951     if (c == (MAX_FORM_COMMAND + 1)
5952         && form_driver(form, REQ_VALIDATION) == E_OK)
5953         return (TRUE);
5954     else {
5955         beep();
5956         return (FALSE);
5957     }
5958 }
5959
5960 #ifdef NCURSES_VERSION
5961 #define FIELDCHECK_CB(func) bool func(FIELD * fld, const void * data GCC_UNUSED)
5962 #define CHAR_CHECK_CB(func) bool func(int ch, const void *data GCC_UNUSED)
5963 #else
5964 #define FIELDCHECK_CB(func) int func(FIELD * fld, char * data GCC_UNUSED)
5965 #define CHAR_CHECK_CB(func) int func(int ch, char *data GCC_UNUSED)
5966 #endif
5967
5968 /*
5969  * Allow a middle initial, optionally with a '.' to end it.
5970  */
5971 static
5972 FIELDCHECK_CB(mi_field_check)
5973 {
5974     char *s = field_buffer(fld, 0);
5975     int state = 0;
5976     int n;
5977
5978     for (n = 0; s[n] != '\0'; ++n) {
5979         switch (state) {
5980         case 0:
5981             if (s[n] == '.') {
5982                 if (n != 1)
5983                     return FALSE;
5984                 state = 2;
5985             } else if (isspace(UChar(s[n]))) {
5986                 state = 2;
5987             }
5988             break;
5989         case 2:
5990             if (!isspace(UChar(s[n])))
5991                 return FALSE;
5992             break;
5993         }
5994     }
5995
5996     /* force the form to display a leading capital */
5997     if (islower(UChar(s[0]))) {
5998         s[0] = (char) toupper(UChar(s[0]));
5999         set_field_buffer(fld, 0, s);
6000     }
6001     return TRUE;
6002 }
6003
6004 static
6005 CHAR_CHECK_CB(mi_char_check)
6006 {
6007     return ((isalpha(ch) || ch == '.') ? TRUE : FALSE);
6008 }
6009
6010 /*
6011  * Passwords should be at least 6 characters.
6012  */
6013 static
6014 FIELDCHECK_CB(pw_field_check)
6015 {
6016     char *s = field_buffer(fld, 0);
6017     int n;
6018
6019     for (n = 0; s[n] != '\0'; ++n) {
6020         if (isspace(UChar(s[n]))) {
6021             if (n < 6)
6022                 return FALSE;
6023         }
6024     }
6025     return TRUE;
6026 }
6027
6028 static
6029 CHAR_CHECK_CB(pw_char_check)
6030 {
6031     return (isgraph(ch) ? TRUE : FALSE);
6032 }
6033
6034 static void
6035 demo_forms(void)
6036 {
6037     WINDOW *w;
6038     FORM *form;
6039     FIELD *f[12], *secure;
6040     FIELDTYPE *fty_middle = new_fieldtype(mi_field_check, mi_char_check);
6041     FIELDTYPE *fty_passwd = new_fieldtype(pw_field_check, pw_char_check);
6042     int finished = 0, c;
6043     unsigned n = 0;
6044
6045 #ifdef NCURSES_MOUSE_VERSION
6046     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
6047 #endif
6048
6049     move(18, 0);
6050     addstr("Defined edit/traversal keys:   ^Q/ESC- exit form\n");
6051     addstr("^N   -- go to next field       ^P  -- go to previous field\n");
6052     addstr("Home -- go to first field      End -- go to last field\n");
6053     addstr("^L   -- go to field to left    ^R  -- go to field to right\n");
6054     addstr("^U   -- move upward to field   ^D  -- move downward to field\n");
6055     addstr("^W   -- go to next word        ^B  -- go to previous word\n");
6056     addstr("^S   -- go to start of field   ^E  -- go to end of field\n");
6057     addstr("^H   -- delete previous char   ^Y  -- delete line\n");
6058     addstr("^G   -- delete current word    ^C  -- clear to end of line\n");
6059     addstr("^K   -- clear to end of field  ^X  -- clear field\n");
6060     addstr("Arrow keys move within a field as you would expect. ^] toggles overlay mode.");
6061
6062     MvAddStr(4, 57, "Forms Entry Test");
6063
6064     refresh();
6065
6066     /* describe the form */
6067     memset(f, 0, sizeof(f));
6068     f[n++] = make_label(0, 15, "Sample Form");
6069
6070     f[n++] = make_label(2, 0, "Last Name");
6071     f[n++] = make_field(3, 0, 1, 18, FALSE);
6072     set_field_type(f[n - 1], TYPE_ALPHA, 1);
6073
6074     f[n++] = make_label(2, 20, "First Name");
6075     f[n++] = make_field(3, 20, 1, 12, FALSE);
6076     set_field_type(f[n - 1], TYPE_ALPHA, 1);
6077
6078     f[n++] = make_label(2, 34, "Middle Name");
6079     f[n++] = make_field(3, 34, 1, 12, FALSE);
6080     set_field_type(f[n - 1], fty_middle);
6081
6082     f[n++] = make_label(5, 0, "Comments");
6083     f[n++] = make_field(6, 0, 4, 46, FALSE);
6084
6085     f[n++] = make_label(5, 20, "Password:");
6086     secure =
6087         f[n++] = make_field(5, 30, 1, 9, TRUE);
6088     set_field_type(f[n - 1], fty_passwd);
6089     f[n] = (FIELD *) 0;
6090
6091     if ((form = new_form(f)) != 0) {
6092
6093         display_form(form);
6094
6095         w = form_win(form);
6096         raw();
6097         nonl();                 /* lets us read ^M's */
6098         while (!finished) {
6099             switch (form_driver(form, c = form_virtualize(form, w))) {
6100             case E_OK:
6101                 MvAddStr(5, 57, field_buffer(secure, 1));
6102                 clrtoeol();
6103                 refresh();
6104                 break;
6105             case E_UNKNOWN_COMMAND:
6106                 finished = my_form_driver(form, c);
6107                 break;
6108             default:
6109                 beep();
6110                 break;
6111             }
6112         }
6113
6114         erase_form(form);
6115
6116         free_form(form);
6117     }
6118     for (c = 0; f[c] != 0; c++)
6119         free_field(f[c]);
6120     free_fieldtype(fty_middle);
6121     free_fieldtype(fty_passwd);
6122     noraw();
6123     nl();
6124
6125 #ifdef NCURSES_MOUSE_VERSION
6126     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
6127 #endif
6128 }
6129 #endif /* USE_LIBFORM */
6130
6131 /****************************************************************************
6132  *
6133  * Overlap test
6134  *
6135  ****************************************************************************/
6136
6137 static void
6138 fillwin(WINDOW *win, char ch)
6139 {
6140     int y, x;
6141     int y1, x1;
6142
6143     getmaxyx(win, y1, x1);
6144     for (y = 0; y < y1; y++) {
6145         wmove(win, y, 0);
6146         for (x = 0; x < x1; x++)
6147             waddch(win, UChar(ch));
6148     }
6149 }
6150
6151 static void
6152 crosswin(WINDOW *win, char ch)
6153 {
6154     int y, x;
6155     int y1, x1;
6156
6157     getmaxyx(win, y1, x1);
6158     for (y = 0; y < y1; y++) {
6159         for (x = 0; x < x1; x++)
6160             if (((x > (x1 - 1) / 3) && (x <= (2 * (x1 - 1)) / 3))
6161                 || (((y > (y1 - 1) / 3) && (y <= (2 * (y1 - 1)) / 3)))) {
6162                 wmove(win, y, x);
6163                 waddch(win, UChar(ch));
6164             }
6165     }
6166 }
6167
6168 #define OVERLAP_FLAVORS 5
6169
6170 static void
6171 overlap_helpitem(int state, int item, char *message)
6172 {
6173     int row = (item / 2);
6174     int col = ((item % 2) ? COLS / 2 : 0);
6175
6176     move(LINES - 6 + row, col);
6177     printw("%c%c = %s", state == row ? '>' : ' ', 'a' + item, message);
6178     clrtoeol();
6179 }
6180
6181 static void
6182 overlap_test_1_attr(WINDOW *win, int flavor, int col)
6183 {
6184     short cpair = (short) (1 + (flavor * 2) + col);
6185
6186     switch (flavor) {
6187     case 0:
6188         (void) wattrset(win, A_NORMAL);
6189         break;
6190     case 1:
6191         (void) wattrset(win, A_BOLD);
6192         break;
6193     case 2:
6194         init_pair(cpair, COLOR_BLUE, COLOR_WHITE);
6195         (void) wattrset(win, (int) (COLOR_PAIR(cpair) | A_NORMAL));
6196         break;
6197     case 3:
6198         init_pair(cpair, COLOR_WHITE, COLOR_BLUE);
6199         (void) wattrset(win, (int) (COLOR_PAIR(cpair) | A_BOLD));
6200         break;
6201     }
6202 }
6203
6204 static void
6205 overlap_test_2_attr(WINDOW *win, int flavor, int col)
6206 {
6207     short cpair = (short) (9 + (flavor * 2) + col);
6208
6209     switch (flavor) {
6210     case 0:
6211         /* no effect */
6212         break;
6213     case 1:
6214         /* no effect */
6215         break;
6216     case 2:
6217         init_pair(cpair, COLOR_RED, COLOR_GREEN);
6218         wbkgdset(win, colored_chtype(' ', A_BLINK, cpair));
6219         break;
6220     case 3:
6221         wbkgdset(win, ' ' | A_NORMAL);
6222         break;
6223     }
6224 }
6225
6226 static int
6227 overlap_help(int state, int flavors[OVERLAP_FLAVORS])
6228 {
6229     int row;
6230     int col;
6231     int item;
6232     const char *ths, *tht;
6233     char msg[80];
6234
6235     if (state < 0)
6236         state += OVERLAP_FLAVORS;
6237     state = state % OVERLAP_FLAVORS;
6238     assert(state >= 0 && state < OVERLAP_FLAVORS);
6239
6240     for (item = 0; item < (2 * OVERLAP_FLAVORS); ++item) {
6241         row = item / 2;
6242         col = item % 2;
6243         ths = col ? "B" : "A";
6244         tht = col ? "A" : "B";
6245
6246         switch (row) {
6247         case 0:
6248             flavors[row] = 0;
6249             sprintf(msg, "refresh %s, then %s, then doupdate.", ths, tht);
6250             break;
6251         case 1:
6252             if (use_colors) {
6253                 flavors[row] %= 4;
6254             } else {
6255                 flavors[row] %= 2;
6256             }
6257             overlap_test_1_attr(stdscr, flavors[row], col);
6258             sprintf(msg, "fill window %s with letter %s.", ths, ths);
6259             break;
6260         case 2:
6261             if (use_colors) {
6262                 flavors[row] %= 4;
6263             } else {
6264                 flavors[row] %= 2;
6265             }
6266             switch (flavors[row]) {
6267             case 0:
6268                 sprintf(msg, "cross pattern in window %s.", ths);
6269                 break;
6270             case 1:
6271                 sprintf(msg, "draw box in window %s.", ths);
6272                 break;
6273             case 2:
6274                 sprintf(msg, "set background of window %s.", ths);
6275                 break;
6276             case 3:
6277                 sprintf(msg, "reset background of window %s.", ths);
6278                 break;
6279             }
6280             break;
6281         case 3:
6282             flavors[row] = 0;
6283             sprintf(msg, "clear window %s.", ths);
6284             break;
6285         case 4:
6286             flavors[row] %= 4;
6287             switch (flavors[row]) {
6288             case 0:
6289                 sprintf(msg, "overwrite %s onto %s.", ths, tht);
6290                 break;
6291             case 1:
6292                 sprintf(msg, "copywin(FALSE) %s onto %s.", ths, tht);
6293                 break;
6294             case 2:
6295                 sprintf(msg, "copywin(TRUE) %s onto %s.", ths, tht);
6296                 break;
6297             case 3:
6298                 sprintf(msg, "overlay %s onto %s.", ths, tht);
6299                 break;
6300             }
6301             break;
6302         }
6303         overlap_helpitem(state, item, msg);
6304         (void) wattrset(stdscr, A_NORMAL);
6305         wbkgdset(stdscr, ' ' | A_NORMAL);
6306     }
6307     move(LINES - 1, 0);
6308     printw("^Q/ESC = terminate test.  Up/down/space select test variations (%d %d).",
6309            state, flavors[state]);
6310
6311     return state;
6312 }
6313
6314 static void
6315 overlap_test_0(WINDOW *a, WINDOW *b)
6316 {
6317     touchwin(a);
6318     touchwin(b);
6319     wnoutrefresh(a);
6320     wnoutrefresh(b);
6321     doupdate();
6322 }
6323
6324 static void
6325 overlap_test_1(int flavor, int col, WINDOW *a, char fill)
6326 {
6327     overlap_test_1_attr(a, flavor, col);
6328     fillwin(a, fill);
6329     (void) wattrset(a, A_NORMAL);
6330 }
6331
6332 static void
6333 overlap_test_2(int flavor, int col, WINDOW *a, char fill)
6334 {
6335     overlap_test_2_attr(a, flavor, col);
6336     switch (flavor) {
6337     case 0:
6338         crosswin(a, fill);
6339         break;
6340     case 1:
6341         box(a, 0, 0);
6342         break;
6343     case 2:
6344         /* done in overlap_test_2_attr */
6345         break;
6346     case 3:
6347         /* done in overlap_test_2_attr */
6348         break;
6349     }
6350 }
6351
6352 static void
6353 overlap_test_3(WINDOW *a)
6354 {
6355     wclear(a);
6356     wmove(a, 0, 0);
6357 }
6358
6359 static void
6360 overlap_test_4(int flavor, WINDOW *a, WINDOW *b)
6361 {
6362     switch (flavor) {
6363     case 0:
6364         overwrite(a, b);
6365         break;
6366     case 1:
6367         copywin(a, b, 0, 0, 0, 0, getmaxy(b), getmaxx(b), FALSE);
6368         break;
6369     case 2:
6370         copywin(a, b, 0, 0, 0, 0, getmaxy(b), getmaxx(b), TRUE);
6371         break;
6372     case 3:
6373         overlay(a, b);
6374         break;
6375     }
6376 }
6377
6378 /* test effects of overlapping windows */
6379 static void
6380 overlap_test(void)
6381 {
6382     int ch;
6383     int state, flavor[OVERLAP_FLAVORS];
6384
6385     WINDOW *win1 = newwin(9, 20, 3, 3);
6386     WINDOW *win2 = newwin(9, 20, 9, 16);
6387
6388     curs_set(0);
6389     raw();
6390     refresh();
6391     move(0, 0);
6392     printw("This test shows the behavior of wnoutrefresh() with respect to\n");
6393     printw("the shared region of two overlapping windows A and B.  The cross\n");
6394     printw("pattern in each window does not overlap the other.\n");
6395
6396     memset(flavor, 0, sizeof(flavor));
6397     state = overlap_help(0, flavor);
6398
6399     while (!isQuit(ch = Getchar()))
6400         switch (ch) {
6401         case 'a':               /* refresh window A first, then B */
6402             overlap_test_0(win1, win2);
6403             break;
6404
6405         case 'b':               /* refresh window B first, then A */
6406             overlap_test_0(win2, win1);
6407             break;
6408
6409         case 'c':               /* fill window A so it's visible */
6410             overlap_test_1(flavor[1], 0, win1, 'A');
6411             break;
6412
6413         case 'd':               /* fill window B so it's visible */
6414             overlap_test_1(flavor[1], 1, win2, 'B');
6415             break;
6416
6417         case 'e':               /* cross test pattern in window A */
6418             overlap_test_2(flavor[2], 0, win1, 'A');
6419             break;
6420
6421         case 'f':               /* cross test pattern in window A */
6422             overlap_test_2(flavor[2], 1, win2, 'B');
6423             break;
6424
6425         case 'g':               /* clear window A */
6426             overlap_test_3(win1);
6427             break;
6428
6429         case 'h':               /* clear window B */
6430             overlap_test_3(win2);
6431             break;
6432
6433         case 'i':               /* overwrite A onto B */
6434             overlap_test_4(flavor[4], win1, win2);
6435             break;
6436
6437         case 'j':               /* overwrite B onto A */
6438             overlap_test_4(flavor[4], win2, win1);
6439             break;
6440
6441         case CTRL('n'):
6442         case KEY_DOWN:
6443             state = overlap_help(state + 1, flavor);
6444             break;
6445
6446         case CTRL('p'):
6447         case KEY_UP:
6448             state = overlap_help(state - 1, flavor);
6449             break;
6450
6451         case ' ':
6452             flavor[state] += 1;
6453             state = overlap_help(state, flavor);
6454             break;
6455
6456         case '?':
6457             state = overlap_help(state, flavor);
6458             break;
6459
6460         default:
6461             beep();
6462             break;
6463         }
6464
6465     delwin(win2);
6466     delwin(win1);
6467     erase();
6468     curs_set(1);
6469     endwin();
6470 }
6471
6472 /****************************************************************************
6473  *
6474  * Main sequence
6475  *
6476  ****************************************************************************/
6477
6478 static bool
6479 do_single_test(const char c)
6480 /* perform a single specified test */
6481 {
6482     switch (c) {
6483     case 'a':
6484         getch_test();
6485         break;
6486
6487 #if USE_WIDEC_SUPPORT
6488     case 'A':
6489         get_wch_test();
6490         break;
6491 #endif
6492
6493     case 'b':
6494         attr_test();
6495         break;
6496
6497 #if USE_WIDEC_SUPPORT
6498     case 'B':
6499         wide_attr_test();
6500         break;
6501 #endif
6502
6503     case 'c':
6504         if (!use_colors)
6505             Cannot("does not support color.");
6506         else
6507             color_test();
6508         break;
6509
6510 #if USE_WIDEC_SUPPORT
6511     case 'C':
6512         if (!use_colors)
6513             Cannot("does not support color.");
6514         else
6515             wide_color_test();
6516         break;
6517 #endif
6518
6519     case 'd':
6520         if (!use_colors)
6521             Cannot("does not support color.");
6522         else if (!can_change_color())
6523             Cannot("has hardwired color values.");
6524         else
6525             color_edit();
6526         break;
6527
6528 #if USE_SOFTKEYS
6529     case 'e':
6530         slk_test();
6531         break;
6532
6533 #if USE_WIDEC_SUPPORT
6534     case 'E':
6535         wide_slk_test();
6536         break;
6537 #endif
6538 #endif
6539
6540     case 'f':
6541         acs_display();
6542         break;
6543
6544 #if USE_WIDEC_SUPPORT
6545     case 'F':
6546         wide_acs_display();
6547         break;
6548 #endif
6549
6550 #if USE_LIBPANEL
6551     case 'o':
6552         demo_panels(init_panel, fill_panel);
6553         break;
6554 #endif
6555
6556 #if USE_WIDEC_SUPPORT && USE_LIBPANEL
6557     case 'O':
6558         demo_panels(init_wide_panel, fill_wide_panel);
6559         break;
6560 #endif
6561
6562     case 'g':
6563         acs_and_scroll();
6564         break;
6565
6566     case 'i':
6567         flushinp_test(stdscr);
6568         break;
6569
6570     case 'k':
6571         test_sgr_attributes();
6572         break;
6573
6574 #if USE_LIBMENU
6575     case 'm':
6576         menu_test();
6577         break;
6578 #endif
6579
6580     case 'p':
6581         demo_pad();
6582         break;
6583
6584 #if USE_LIBFORM
6585     case 'r':
6586         demo_forms();
6587         break;
6588 #endif
6589
6590     case 's':
6591         overlap_test();
6592         break;
6593
6594 #if USE_LIBMENU && defined(TRACE)
6595     case 't':
6596         trace_set();
6597         break;
6598 #endif
6599
6600     case '?':
6601         break;
6602
6603     default:
6604         return FALSE;
6605     }
6606
6607     return TRUE;
6608 }
6609
6610 static void
6611 usage(void)
6612 {
6613     static const char *const tbl[] =
6614     {
6615         "Usage: ncurses [options]"
6616         ,""
6617         ,"Options:"
6618 #ifdef NCURSES_VERSION
6619         ,"  -a f,b   set default-colors (assumed white-on-black)"
6620         ,"  -d       use default-colors if terminal supports them"
6621 #endif
6622         ,"  -E       call use_env(FALSE) to ignore $LINES and $COLUMNS"
6623 #if USE_SOFTKEYS
6624         ,"  -e fmt   specify format for soft-keys test (e)"
6625 #endif
6626 #if HAVE_RIPOFFLINE
6627         ,"  -f       rip-off footer line (can repeat)"
6628         ,"  -h       rip-off header line (can repeat)"
6629 #endif
6630         ,"  -m       do not use colors"
6631         ,"  -p file  rgb values to use in 'd' rather than ncurses's builtin"
6632 #if USE_LIBPANEL
6633         ,"  -s msec  specify nominal time for panel-demo (default: 1, to hold)"
6634 #endif
6635 #if defined(NCURSES_VERSION_PATCH) && (NCURSES_VERSION_PATCH >= 20120714)
6636         ,"  -T       call use_tioctl(TRUE) to allow SIGWINCH to override environment"
6637 #endif
6638 #ifdef TRACE
6639         ,"  -t mask  specify default trace-level (may toggle with ^T)"
6640 #endif
6641     };
6642     size_t n;
6643     for (n = 0; n < SIZEOF(tbl); n++)
6644         fprintf(stderr, "%s\n", tbl[n]);
6645     ExitProgram(EXIT_FAILURE);
6646 }
6647
6648 static void
6649 set_terminal_modes(void)
6650 {
6651     noraw();
6652     cbreak();
6653     noecho();
6654     scrollok(stdscr, TRUE);
6655     idlok(stdscr, TRUE);
6656     keypad(stdscr, TRUE);
6657 }
6658
6659 #ifdef SIGUSR1
6660 static RETSIGTYPE
6661 announce_sig(int sig)
6662 {
6663     (void) fprintf(stderr, "Handled signal %d\r\n", sig);
6664 }
6665 #endif
6666
6667 #if HAVE_RIPOFFLINE
6668 static int
6669 rip_footer(WINDOW *win, int cols)
6670 {
6671     wbkgd(win, A_REVERSE);
6672     werase(win);
6673     wmove(win, 0, 0);
6674     wprintw(win, "footer: window %p, %d columns", (void *) win, cols);
6675     wnoutrefresh(win);
6676     return OK;
6677 }
6678
6679 static int
6680 rip_header(WINDOW *win, int cols)
6681 {
6682     wbkgd(win, A_REVERSE);
6683     werase(win);
6684     wmove(win, 0, 0);
6685     wprintw(win, "header: window %p, %d columns", (void *) win, cols);
6686     wnoutrefresh(win);
6687     return OK;
6688 }
6689 #endif /* HAVE_RIPOFFLINE */
6690
6691 static void
6692 main_menu(bool top)
6693 {
6694     char command;
6695
6696     do {
6697         (void) puts("This is the ncurses main menu");
6698         (void) puts("a = keyboard and mouse input test");
6699 #if USE_WIDEC_SUPPORT
6700         (void) puts("A = wide-character keyboard and mouse input test");
6701 #endif
6702         (void) puts("b = character attribute test");
6703 #if USE_WIDEC_SUPPORT
6704         (void) puts("B = wide-character attribute test");
6705 #endif
6706         (void) puts("c = color test pattern");
6707 #if USE_WIDEC_SUPPORT
6708         (void) puts("C = color test pattern using wide-character calls");
6709 #endif
6710         if (top)
6711             (void) puts("d = edit RGB color values");
6712 #if USE_SOFTKEYS
6713         (void) puts("e = exercise soft keys");
6714 #if USE_WIDEC_SUPPORT
6715         (void) puts("E = exercise soft keys using wide-characters");
6716 #endif
6717 #endif
6718         (void) puts("f = display ACS characters");
6719 #if USE_WIDEC_SUPPORT
6720         (void) puts("F = display Wide-ACS characters");
6721 #endif
6722         (void) puts("g = display windows and scrolling");
6723         (void) puts("i = test of flushinp()");
6724         (void) puts("k = display character attributes");
6725 #if USE_LIBMENU
6726         (void) puts("m = menu code test");
6727 #endif
6728 #if USE_LIBPANEL
6729         (void) puts("o = exercise panels library");
6730 #if USE_WIDEC_SUPPORT
6731         (void) puts("O = exercise panels with wide-characters");
6732 #endif
6733 #endif
6734         (void) puts("p = exercise pad features");
6735         (void) puts("q = quit");
6736 #if USE_LIBFORM
6737         (void) puts("r = exercise forms code");
6738 #endif
6739         (void) puts("s = overlapping-refresh test");
6740 #if USE_LIBMENU && defined(TRACE)
6741         (void) puts("t = set trace level");
6742 #endif
6743         (void) puts("? = repeat this command summary");
6744
6745         (void) fputs("> ", stdout);
6746         (void) fflush(stdout);  /* necessary under SVr4 curses */
6747
6748         /*
6749          * This used to be an 'fgets()' call (until 1996/10).  However with
6750          * some runtime libraries, mixing stream I/O and 'read()' causes the
6751          * input stream to be flushed when switching between the two.
6752          */
6753         command = 0;
6754         for (;;) {
6755             char ch = '\0';
6756             if (read(fileno(stdin), &ch, 1) <= 0) {
6757                 if (command == 0)
6758                     command = 'q';
6759                 break;
6760             } else if (command == 0 && !isspace(UChar(ch))) {
6761                 command = ch;
6762             } else if (ch == '\n' || ch == '\r') {
6763                 if ((command == 'd') && !top) {
6764                     (void) fputs("Do not nest test-d\n", stdout);
6765                     command = 0;
6766                 }
6767                 if (command != 0)
6768                     break;
6769                 (void) fputs("> ", stdout);
6770                 (void) fflush(stdout);
6771             }
6772         }
6773
6774         if (do_single_test(command)) {
6775             /*
6776              * This may be overkill; it's intended to reset everything back
6777              * to the initial terminal modes so that tests don't get in
6778              * each other's way.
6779              */
6780             flushinp();
6781             set_terminal_modes();
6782             reset_prog_mode();
6783             clear();
6784             refresh();
6785             endwin();
6786             if (command == '?') {
6787                 (void) puts("This is the ncurses capability tester.");
6788                 (void)
6789                     puts("You may select a test from the main menu by typing the");
6790                 (void)
6791                     puts("key letter of the choice (the letter to left of the =)");
6792                 (void)
6793                     puts("at the > prompt.  Type `q' to exit.");
6794             }
6795             continue;
6796         }
6797     } while
6798         (command != 'q');
6799 }
6800
6801 /*+-------------------------------------------------------------------------
6802         main(argc,argv)
6803 --------------------------------------------------------------------------*/
6804
6805 #define okCOLOR(n) ((n) >= 0 && (n) < max_colors)
6806 #define okRGB(n)   ((n) >= 0 && (n) <= 1000)
6807
6808 int
6809 main(int argc, char *argv[])
6810 {
6811     int c;
6812     int my_e_param = 1;
6813 #ifdef NCURSES_VERSION
6814     int default_fg = COLOR_WHITE;
6815     int default_bg = COLOR_BLACK;
6816     bool assumed_colors = FALSE;
6817     bool default_colors = FALSE;
6818 #endif
6819     char *palette_file = 0;
6820     bool monochrome = FALSE;
6821
6822     setlocale(LC_ALL, "");
6823
6824     while ((c = getopt(argc, argv, "a:dEe:fhmp:s:Tt:")) != -1) {
6825         switch (c) {
6826 #ifdef NCURSES_VERSION
6827         case 'a':
6828             assumed_colors = TRUE;
6829             sscanf(optarg, "%d,%d", &default_fg, &default_bg);
6830             break;
6831         case 'd':
6832             default_colors = TRUE;
6833             break;
6834 #endif
6835         case 'E':
6836             use_env(FALSE);
6837             break;
6838         case 'e':
6839             my_e_param = atoi(optarg);
6840 #ifdef NCURSES_VERSION
6841             if (my_e_param > 3) /* allow extended layouts */
6842                 usage();
6843 #else
6844             if (my_e_param > 1)
6845                 usage();
6846 #endif
6847             break;
6848 #if HAVE_RIPOFFLINE
6849         case 'f':
6850             ripoffline(-1, rip_footer);
6851             break;
6852         case 'h':
6853             ripoffline(1, rip_header);
6854             break;
6855 #endif /* HAVE_RIPOFFLINE */
6856         case 'm':
6857             monochrome = TRUE;
6858             break;
6859         case 'p':
6860             palette_file = optarg;
6861             break;
6862 #if USE_LIBPANEL
6863         case 's':
6864             nap_msec = (int) atol(optarg);
6865             break;
6866 #endif
6867 #if defined(NCURSES_VERSION_PATCH) && (NCURSES_VERSION_PATCH >= 20120714)
6868         case 'T':
6869             use_tioctl(TRUE);
6870             break;
6871 #endif
6872 #ifdef TRACE
6873         case 't':
6874             save_trace = (unsigned) strtol(optarg, 0, 0);
6875             break;
6876 #endif
6877         default:
6878             usage();
6879         }
6880     }
6881
6882     /*
6883      * If there's no menus (unlikely for ncurses!), then we'll have to set
6884      * tracing on initially, just in case the user wants to test something that
6885      * doesn't involve wGetchar.
6886      */
6887 #ifdef TRACE
6888     /* enable debugging */
6889 #if !USE_LIBMENU
6890     trace(save_trace);
6891 #else
6892     if (!isatty(fileno(stdin)))
6893         trace(save_trace);
6894 #endif /* USE_LIBMENU */
6895 #endif /* TRACE */
6896
6897 #if USE_SOFTKEYS
6898     /* tell it we're going to play with soft keys */
6899     slk_init(my_e_param);
6900 #endif
6901
6902 #ifdef SIGUSR1
6903     /* set up null signal catcher so we can see what interrupts to getch do */
6904     signal(SIGUSR1, announce_sig);
6905 #endif
6906
6907     /* we must initialize the curses data structure only once */
6908     initscr();
6909     bkgdset(BLANK);
6910
6911     /* tests, in general, will want these modes */
6912     use_colors = monochrome ? FALSE : has_colors();
6913
6914     if (use_colors) {
6915         start_color();
6916 #ifdef NCURSES_VERSION_PATCH
6917         max_colors = COLORS;    /* was > 16 ? 16 : COLORS */
6918 #if HAVE_USE_DEFAULT_COLORS
6919         if (default_colors) {
6920             use_default_colors();
6921             min_colors = -1;
6922         }
6923 #if HAVE_ASSUME_DEFAULT_COLORS
6924         if (assumed_colors)
6925             assume_default_colors(default_fg, default_bg);
6926 #endif
6927 #endif
6928 #else /* normal SVr4 curses */
6929         max_colors = COLORS;    /* was > 8 ? 8 : COLORS */
6930 #endif
6931         max_pairs = COLOR_PAIRS;        /* was > 256 ? 256 : COLOR_PAIRS */
6932
6933         if (can_change_color()) {
6934             short cp;
6935             all_colors = typeMalloc(RGB_DATA, (unsigned) max_colors);
6936             if (!all_colors)
6937                 failed("all_colors");
6938             for (cp = 0; cp < max_colors; ++cp) {
6939                 color_content(cp,
6940                               &all_colors[cp].red,
6941                               &all_colors[cp].green,
6942                               &all_colors[cp].blue);
6943             }
6944             if (palette_file != 0) {
6945                 FILE *fp = fopen(palette_file, "r");
6946                 if (fp != 0) {
6947                     char buffer[BUFSIZ];
6948                     int red, green, blue;
6949                     int scale = 1000;
6950                     while (fgets(buffer, sizeof(buffer), fp) != 0) {
6951                         if (sscanf(buffer, "scale:%d", &c) == 1) {
6952                             scale = c;
6953                         } else if (sscanf(buffer, "%d:%d %d %d",
6954                                           &c,
6955                                           &red,
6956                                           &green,
6957                                           &blue) == 4
6958                                    && okCOLOR(c)
6959                                    && okRGB(red)
6960                                    && okRGB(green)
6961                                    && okRGB(blue)) {
6962                             all_colors[c].red = (short) ((red * 1000) / scale);
6963                             all_colors[c].green = (short) ((green * 1000) / scale);
6964                             all_colors[c].blue = (short) ((blue * 1000) / scale);
6965                         }
6966                     }
6967                     fclose(fp);
6968                 }
6969             }
6970         }
6971     }
6972     set_terminal_modes();
6973     def_prog_mode();
6974
6975     /*
6976      * Return to terminal mode, so we're guaranteed of being able to
6977      * select terminal commands even if the capabilities are wrong.
6978      */
6979     endwin();
6980
6981 #if HAVE_CURSES_VERSION
6982     (void) printf("Welcome to %s.  Press ? for help.\n", curses_version());
6983 #elif defined(NCURSES_VERSION_MAJOR) && defined(NCURSES_VERSION_MINOR) && defined(NCURSES_VERSION_PATCH)
6984     (void) printf("Welcome to ncurses %d.%d.%d.  Press ? for help.\n",
6985                   NCURSES_VERSION_MAJOR,
6986                   NCURSES_VERSION_MINOR,
6987                   NCURSES_VERSION_PATCH);
6988 #else
6989     (void) puts("Welcome to ncurses.  Press ? for help.");
6990 #endif
6991
6992     main_menu(TRUE);
6993
6994     ExitProgram(EXIT_SUCCESS);
6995 }
6996
6997 /* ncurses.c ends here */