]> ncurses.scripts.mit.edu Git - ncurses.git/blob - test/ncurses.c
4722e3f871fd38de89486c0497d4dd6ed2beb5a8
[ncurses.git] / test / ncurses.c
1 /****************************************************************************
2  * Copyright (c) 1998-2012,2013 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.389 2013/04/27 19:46:53 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             sprintf(temp, "%.*s", 16, the_color_names[color]);
1923         }
1924         printw("%-*.*s", width, width, temp);
1925     }
1926 }
1927
1928 static void
1929 color_legend(WINDOW *helpwin, bool wide)
1930 {
1931     int row = 1;
1932     int col = 1;
1933
1934     MvWPrintw(helpwin, row++, col,
1935               "ESC to exit.");
1936     ++row;
1937     MvWPrintw(helpwin, row++, col,
1938               "Use up/down arrow to scroll through the display if it is");
1939     MvWPrintw(helpwin, row++, col,
1940               "longer than one screen. Control/N and Control/P can be used");
1941     MvWPrintw(helpwin, row++, col,
1942               "in place of up/down arrow.  Use pageup/pagedown to scroll a");
1943     MvWPrintw(helpwin, row++, col,
1944               "full screen; control/B and control/F can be used here.");
1945     ++row;
1946     MvWPrintw(helpwin, row++, col,
1947               "Toggles:");
1948     MvWPrintw(helpwin, row++, col,
1949               "  a/A     toggle altcharset off/on");
1950     MvWPrintw(helpwin, row++, col,
1951               "  b/B     toggle bold off/on");
1952     MvWPrintw(helpwin, row++, col,
1953               "  n/N     toggle text/number on/off");
1954     MvWPrintw(helpwin, row++, col,
1955               "  r/R     toggle reverse on/off");
1956     MvWPrintw(helpwin, row++, col,
1957               "  w/W     toggle width between 8/16 colors");
1958 #if USE_WIDEC_SUPPORT
1959     if (wide) {
1960         MvWPrintw(helpwin, row++, col,
1961                   "Wide characters:");
1962         MvWPrintw(helpwin, row, col,
1963                   "  x/X     toggle text between ASCII and wide-character");
1964     }
1965 #else
1966     (void) wide;
1967 #endif
1968 }
1969
1970 #define set_color_test(name, value) if (name != value) { name = value; base_row = 0; }
1971
1972 /* generate a color test pattern */
1973 static void
1974 color_test(void)
1975 {
1976     short i;
1977     int top = 0, width;
1978     int base_row = 0;
1979     int grid_top = top + 3;
1980     int page_size = (LINES - grid_top);
1981     int pairs_max = PAIR_NUMBER(A_COLOR) + 1;
1982     int row_limit;
1983     int per_row;
1984     char numbered[80];
1985     const char *hello;
1986     bool done = FALSE;
1987     bool opt_acsc = FALSE;
1988     bool opt_bold = FALSE;
1989     bool opt_revs = FALSE;
1990     bool opt_nums = FALSE;
1991     bool opt_wide = FALSE;
1992     WINDOW *helpwin;
1993
1994     if (COLORS * COLORS == COLOR_PAIRS) {
1995         int limit = (COLORS - min_colors) * (COLORS - min_colors);
1996         if (pairs_max > limit)
1997             pairs_max = limit;
1998     } else {
1999         if (pairs_max > COLOR_PAIRS)
2000             pairs_max = COLOR_PAIRS;
2001     }
2002
2003     while (!done) {
2004         int shown = 0;
2005
2006         /* this assumes an 80-column line */
2007         if (opt_wide) {
2008             width = 4;
2009             hello = "Test";
2010             per_row = (COLORS > 8) ? 16 : 8;
2011         } else {
2012             width = 8;
2013             hello = "Hello";
2014             per_row = 8;
2015         }
2016         per_row -= min_colors;
2017
2018         row_limit = (pairs_max + per_row - 1) / per_row;
2019
2020         move(0, 0);
2021         (void) printw("There are %d color pairs and %d colors%s\n",
2022                       pairs_max, COLORS,
2023                       min_colors ? " besides 'default'" : "");
2024
2025         clrtobot();
2026         MvPrintw(top + 1, 0,
2027                  "%dx%d matrix of foreground/background colors, bold *%s*\n",
2028                  row_limit,
2029                  per_row,
2030                  opt_bold ? "on" : "off");
2031
2032         /* show color names/numbers across the top */
2033         for (i = 0; i < per_row; i++)
2034             show_color_name(top + 2, (i + 1) * width, i + min_colors, opt_wide);
2035
2036         /* show a grid of colors, with color names/ numbers on the left */
2037         for (i = (short) (base_row * per_row); i < pairs_max; i++) {
2038             int row = grid_top + (i / per_row) - base_row;
2039             int col = (i % per_row + 1) * width;
2040             short pair = i;
2041
2042 #define InxToFG(i) (short) ((i % (COLORS - min_colors)) + min_colors)
2043 #define InxToBG(i) (short) ((i / (COLORS - min_colors)) + min_colors)
2044             if (row >= 0 && move(row, col) != ERR) {
2045                 short fg = InxToFG(i);
2046                 short bg = InxToBG(i);
2047
2048                 init_pair(pair, fg, bg);
2049                 attron((attr_t) COLOR_PAIR(pair));
2050                 if (opt_acsc)
2051                     attron((attr_t) A_ALTCHARSET);
2052                 if (opt_bold)
2053                     attron((attr_t) A_BOLD);
2054                 if (opt_revs)
2055                     attron((attr_t) A_REVERSE);
2056
2057                 if (opt_nums) {
2058                     sprintf(numbered, "{%02X}", i);
2059                     hello = numbered;
2060                 }
2061                 printw("%-*.*s", width, width, hello);
2062                 (void) attrset(A_NORMAL);
2063
2064                 if ((i % per_row) == 0 && InxToFG(i) == min_colors) {
2065                     show_color_name(row, 0, InxToBG(i), opt_wide);
2066                 }
2067                 ++shown;
2068             } else if (shown) {
2069                 break;
2070             }
2071         }
2072
2073         switch (wGetchar(stdscr)) {
2074         case 'a':
2075             opt_acsc = FALSE;
2076             break;
2077         case 'A':
2078             opt_acsc = TRUE;
2079             break;
2080         case 'b':
2081             opt_bold = FALSE;
2082             break;
2083         case 'B':
2084             opt_bold = TRUE;
2085             break;
2086         case 'n':
2087             opt_nums = FALSE;
2088             break;
2089         case 'N':
2090             opt_nums = TRUE;
2091             break;
2092         case 'r':
2093             opt_revs = FALSE;
2094             break;
2095         case 'R':
2096             opt_revs = TRUE;
2097             break;
2098         case case_QUIT:
2099             done = TRUE;
2100             continue;
2101         case 'w':
2102             set_color_test(opt_wide, FALSE);
2103             break;
2104         case 'W':
2105             set_color_test(opt_wide, TRUE);
2106             break;
2107         case CTRL('p'):
2108         case KEY_UP:
2109             if (base_row <= 0) {
2110                 beep();
2111             } else {
2112                 base_row -= 1;
2113             }
2114             break;
2115         case CTRL('n'):
2116         case KEY_DOWN:
2117             if (base_row + page_size >= row_limit) {
2118                 beep();
2119             } else {
2120                 base_row += 1;
2121             }
2122             break;
2123         case CTRL('b'):
2124         case KEY_PREVIOUS:
2125         case KEY_PPAGE:
2126             if (base_row <= 0) {
2127                 beep();
2128             } else {
2129                 base_row -= (page_size - 1);
2130                 if (base_row < 0)
2131                     base_row = 0;
2132             }
2133             break;
2134         case CTRL('f'):
2135         case KEY_NEXT:
2136         case KEY_NPAGE:
2137             if (base_row + page_size >= row_limit) {
2138                 beep();
2139             } else {
2140                 base_row += page_size - 1;
2141                 if (base_row + page_size >= row_limit) {
2142                     base_row = row_limit - page_size - 1;
2143                 }
2144             }
2145             break;
2146         case '?':
2147             if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
2148                 box(helpwin, 0, 0);
2149                 color_legend(helpwin, FALSE);
2150                 wGetchar(helpwin);
2151                 delwin(helpwin);
2152             }
2153             break;
2154         default:
2155             beep();
2156             continue;
2157         }
2158     }
2159
2160     erase();
2161     endwin();
2162 }
2163
2164 #if USE_WIDEC_SUPPORT
2165 /* generate a color test pattern */
2166 static void
2167 wide_color_test(void)
2168 {
2169     int i;
2170     int top = 0, width;
2171     int base_row = 0;
2172     int grid_top = top + 3;
2173     int page_size = (LINES - grid_top);
2174     int pairs_max = (unsigned short) (-1);
2175     int row_limit;
2176     int per_row;
2177     char numbered[80];
2178     const char *hello;
2179     bool done = FALSE;
2180     bool opt_acsc = FALSE;
2181     bool opt_bold = FALSE;
2182     bool opt_revs = FALSE;
2183     bool opt_wide = FALSE;
2184     bool opt_nums = FALSE;
2185     bool opt_xchr = FALSE;
2186     wchar_t buffer[80];
2187     WINDOW *helpwin;
2188
2189     if (COLORS * COLORS == COLOR_PAIRS) {
2190         int limit = (COLORS - min_colors) * (COLORS - min_colors);
2191         if (pairs_max > limit)
2192             pairs_max = limit;
2193     } else {
2194         if (pairs_max > COLOR_PAIRS)
2195             pairs_max = COLOR_PAIRS;
2196     }
2197
2198     while (!done) {
2199         int shown = 0;
2200
2201         /* this assumes an 80-column line */
2202         if (opt_wide) {
2203             width = 4;
2204             hello = "Test";
2205             per_row = (COLORS > 8) ? 16 : 8;
2206         } else {
2207             width = 8;
2208             hello = "Hello";
2209             per_row = 8;
2210         }
2211         per_row -= min_colors;
2212
2213         if (opt_xchr) {
2214             make_fullwidth_text(buffer, hello);
2215             width *= 2;
2216             per_row /= 2;
2217         } else {
2218             make_narrow_text(buffer, hello);
2219         }
2220
2221         row_limit = (pairs_max + per_row - 1) / per_row;
2222
2223         move(0, 0);
2224         (void) printw("There are %d color pairs and %d colors%s\n",
2225                       pairs_max, COLORS,
2226                       min_colors ? " besides 'default'" : "");
2227
2228         clrtobot();
2229         MvPrintw(top + 1, 0,
2230                  "%dx%d matrix of foreground/background colors, bold *%s*\n",
2231                  row_limit,
2232                  per_row,
2233                  opt_bold ? "on" : "off");
2234
2235         /* show color names/numbers across the top */
2236         for (i = 0; i < per_row; i++)
2237             show_color_name(top + 2, (i + 1) * width, i + min_colors, opt_wide);
2238
2239         /* show a grid of colors, with color names/ numbers on the left */
2240         for (i = (base_row * per_row); i < pairs_max; i++) {
2241             int row = grid_top + (i / per_row) - base_row;
2242             int col = (i % per_row + 1) * width;
2243             short pair = (short) i;
2244
2245             if (row >= 0 && move(row, col) != ERR) {
2246                 init_pair(pair, InxToFG(i), InxToBG(i));
2247                 (void) color_set(pair, NULL);
2248                 if (opt_acsc)
2249                     attr_on((attr_t) A_ALTCHARSET, NULL);
2250                 if (opt_bold)
2251                     attr_on((attr_t) A_BOLD, NULL);
2252                 if (opt_revs)
2253                     attr_on((attr_t) A_REVERSE, NULL);
2254
2255                 if (opt_nums) {
2256                     sprintf(numbered, "{%02X}", i);
2257                     if (opt_xchr) {
2258                         make_fullwidth_text(buffer, numbered);
2259                     } else {
2260                         make_narrow_text(buffer, numbered);
2261                     }
2262                 }
2263                 addnwstr(buffer, width);
2264                 (void) attr_set(A_NORMAL, 0, NULL);
2265
2266                 if ((i % per_row) == 0 && InxToFG(i) == min_colors) {
2267                     show_color_name(row, 0, InxToBG(i), opt_wide);
2268                 }
2269                 ++shown;
2270             } else if (shown) {
2271                 break;
2272             }
2273         }
2274
2275         switch (wGetchar(stdscr)) {
2276         case 'a':
2277             opt_acsc = FALSE;
2278             break;
2279         case 'A':
2280             opt_acsc = TRUE;
2281             break;
2282         case 'b':
2283             opt_bold = FALSE;
2284             break;
2285         case 'B':
2286             opt_bold = TRUE;
2287             break;
2288         case 'n':
2289             opt_nums = FALSE;
2290             break;
2291         case 'N':
2292             opt_nums = TRUE;
2293             break;
2294         case 'r':
2295             opt_revs = FALSE;
2296             break;
2297         case 'R':
2298             opt_revs = TRUE;
2299             break;
2300         case case_QUIT:
2301             done = TRUE;
2302             continue;
2303         case 'w':
2304             set_color_test(opt_wide, FALSE);
2305             break;
2306         case 'W':
2307             set_color_test(opt_wide, TRUE);
2308             break;
2309         case 'x':
2310             opt_xchr = FALSE;
2311             break;
2312         case 'X':
2313             opt_xchr = TRUE;
2314             break;
2315         case CTRL('p'):
2316         case KEY_UP:
2317             if (base_row <= 0) {
2318                 beep();
2319             } else {
2320                 base_row -= 1;
2321             }
2322             break;
2323         case CTRL('n'):
2324         case KEY_DOWN:
2325             if (base_row + page_size >= row_limit) {
2326                 beep();
2327             } else {
2328                 base_row += 1;
2329             }
2330             break;
2331         case CTRL('b'):
2332         case KEY_PREVIOUS:
2333         case KEY_PPAGE:
2334             if (base_row <= 0) {
2335                 beep();
2336             } else {
2337                 base_row -= (page_size - 1);
2338                 if (base_row < 0)
2339                     base_row = 0;
2340             }
2341             break;
2342         case CTRL('f'):
2343         case KEY_NEXT:
2344         case KEY_NPAGE:
2345             if (base_row + page_size >= row_limit) {
2346                 beep();
2347             } else {
2348                 base_row += page_size - 1;
2349                 if (base_row + page_size >= row_limit) {
2350                     base_row = row_limit - page_size - 1;
2351                 }
2352             }
2353             break;
2354         case '?':
2355             if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
2356                 box(helpwin, 0, 0);
2357                 color_legend(helpwin, TRUE);
2358                 wGetchar(helpwin);
2359                 delwin(helpwin);
2360             }
2361             break;
2362         default:
2363             beep();
2364             continue;
2365         }
2366     }
2367
2368     erase();
2369     endwin();
2370 }
2371 #endif /* USE_WIDEC_SUPPORT */
2372
2373 static void
2374 change_color(short current, int field, int value, int usebase)
2375 {
2376     short red, green, blue;
2377
2378     color_content(current, &red, &green, &blue);
2379
2380     switch (field) {
2381     case 0:
2382         red = (short) (usebase ? (red + value) : value);
2383         break;
2384     case 1:
2385         green = (short) (usebase ? (green + value) : value);
2386         break;
2387     case 2:
2388         blue = (short) (usebase ? (blue + value) : value);
2389         break;
2390     }
2391
2392     if (init_color(current, red, green, blue) == ERR)
2393         beep();
2394 }
2395
2396 static void
2397 init_all_colors(void)
2398 {
2399     short c;
2400
2401     for (c = 0; c < COLORS; ++c)
2402         init_color(c,
2403                    all_colors[c].red,
2404                    all_colors[c].green,
2405                    all_colors[c].blue);
2406 }
2407
2408 #define scaled_rgb(n) ((255 * (n)) / 1000)
2409
2410 static void
2411 color_edit(void)
2412 /* display the color test pattern, without trying to edit colors */
2413 {
2414     int i;
2415     int current = 0;
2416     int this_c = 0, value = 0, field = 0;
2417     int last_c;
2418     int top_color = 0;
2419     int page_size = (LINES - 6);
2420
2421     init_all_colors();
2422     refresh();
2423
2424     for (i = 0; i < max_colors; i++)
2425         init_pair((short) i, (short) COLOR_WHITE, (short) i);
2426
2427     MvPrintw(LINES - 2, 0, "Number: %d", value);
2428
2429     do {
2430         short red, green, blue;
2431
2432         attron(A_BOLD);
2433         MvAddStr(0, 20, "Color RGB Value Editing");
2434         attroff(A_BOLD);
2435
2436         for (i = (short) top_color;
2437              (i - top_color < page_size)
2438              && (i < max_colors); i++) {
2439             char numeric[80];
2440
2441             sprintf(numeric, "[%d]", i);
2442             MvPrintw(2 + i - top_color, 0, "%c %-8s:",
2443                      (i == current ? '>' : ' '),
2444                      (i < (int) SIZEOF(the_color_names)
2445                       ? the_color_names[i] : numeric));
2446             (void) attrset((attr_t) COLOR_PAIR(i));
2447             addstr("        ");
2448             (void) attrset(A_NORMAL);
2449
2450             color_content((short) i, &red, &green, &blue);
2451             addstr("   R = ");
2452             if (current == i && field == 0)
2453                 attron(A_STANDOUT);
2454             printw("%04d", red);
2455             if (current == i && field == 0)
2456                 (void) attrset(A_NORMAL);
2457             addstr(", G = ");
2458             if (current == i && field == 1)
2459                 attron(A_STANDOUT);
2460             printw("%04d", green);
2461             if (current == i && field == 1)
2462                 (void) attrset(A_NORMAL);
2463             addstr(", B = ");
2464             if (current == i && field == 2)
2465                 attron(A_STANDOUT);
2466             printw("%04d", blue);
2467             if (current == i && field == 2)
2468                 (void) attrset(A_NORMAL);
2469             (void) attrset(A_NORMAL);
2470             printw(" ( %3d %3d %3d )",
2471                    scaled_rgb(red),
2472                    scaled_rgb(green),
2473                    scaled_rgb(blue));
2474         }
2475
2476         MvAddStr(LINES - 3, 0,
2477                  "Use up/down to select a color, left/right to change fields.");
2478         MvAddStr(LINES - 2, 0,
2479                  "Modify field by typing nnn=, nnn-, or nnn+.  ? for help.");
2480
2481         move(2 + current - top_color, 0);
2482
2483         last_c = this_c;
2484         this_c = Getchar();
2485         if (this_c < 256 && isdigit(this_c) && !isdigit(last_c))
2486             value = 0;
2487
2488         switch (this_c) {
2489         case CTRL('b'):
2490         case KEY_PPAGE:
2491             if (current > 0)
2492                 current -= (page_size - 1);
2493             else
2494                 beep();
2495             break;
2496
2497         case CTRL('f'):
2498         case KEY_NPAGE:
2499             if (current < (max_colors - 1))
2500                 current += (page_size - 1);
2501             else
2502                 beep();
2503             break;
2504
2505         case CTRL('p'):
2506         case KEY_UP:
2507             current = (current == 0 ? (max_colors - 1) : current - 1);
2508             break;
2509
2510         case CTRL('n'):
2511         case KEY_DOWN:
2512             current = (current == (max_colors - 1) ? 0 : current + 1);
2513             break;
2514
2515         case KEY_RIGHT:
2516             field = (field == 2 ? 0 : field + 1);
2517             break;
2518
2519         case KEY_LEFT:
2520             field = (field == 0 ? 2 : field - 1);
2521             break;
2522
2523         case '0':
2524         case '1':
2525         case '2':
2526         case '3':
2527         case '4':
2528         case '5':
2529         case '6':
2530         case '7':
2531         case '8':
2532         case '9':
2533             value = value * 10 + (this_c - '0');
2534             break;
2535
2536         case '+':
2537             change_color((short) current, field, value, 1);
2538             break;
2539
2540         case '-':
2541             change_color((short) current, field, -value, 1);
2542             break;
2543
2544         case '=':
2545             change_color((short) current, field, value, 0);
2546             break;
2547
2548         case '?':
2549             erase();
2550             P("                      RGB Value Editing Help");
2551             P("");
2552             P("You are in the RGB value editor.  Use the arrow keys to select one of");
2553             P("the fields in one of the RGB triples of the current colors; the one");
2554             P("currently selected will be reverse-video highlighted.");
2555             P("");
2556             P("To change a field, enter the digits of the new value; they are echoed");
2557             P("as entered.  Finish by typing `='.  The change will take effect instantly.");
2558             P("To increment or decrement a value, use the same procedure, but finish");
2559             P("with a `+' or `-'.");
2560             P("");
2561             P("Press 'm' to invoke the top-level menu with the current color settings.");
2562             P("To quit, do ESC");
2563
2564             Pause();
2565             erase();
2566             break;
2567
2568         case 'm':
2569             endwin();
2570             main_menu(FALSE);
2571             for (i = 0; i < max_colors; i++)
2572                 init_pair((short) i, (short) COLOR_WHITE, (short) i);
2573             refresh();
2574             break;
2575
2576         case case_QUIT:
2577             break;
2578
2579         default:
2580             beep();
2581             break;
2582         }
2583
2584         if (current < 0)
2585             current = 0;
2586         if (current >= max_colors)
2587             current = max_colors - 1;
2588         if (current < top_color)
2589             top_color = current;
2590         if (current - top_color >= page_size)
2591             top_color = current - (page_size - 1);
2592
2593         MvPrintw(LINES - 1, 0, "Number: %d", value);
2594         clrtoeol();
2595     } while
2596         (!isQuit(this_c));
2597
2598     erase();
2599
2600     /*
2601      * ncurses does not reset each color individually when calling endwin().
2602      */
2603     init_all_colors();
2604
2605     endwin();
2606 }
2607
2608 /****************************************************************************
2609  *
2610  * Alternate character-set stuff
2611  *
2612  ****************************************************************************/
2613 /* *INDENT-OFF* */
2614 static struct {
2615     chtype attr;
2616     const char *name;
2617 } attrs_to_cycle[] = {
2618     { A_NORMAL,         "normal" },
2619     { A_BOLD,           "bold" },
2620     { A_BLINK,          "blink" },
2621     { A_REVERSE,        "reverse" },
2622     { A_UNDERLINE,      "underline" },
2623 };
2624 /* *INDENT-ON* */
2625
2626 static bool
2627 cycle_attr(int ch, unsigned *at_code, chtype *attr)
2628 {
2629     bool result = TRUE;
2630
2631     switch (ch) {
2632     case 'v':
2633         if ((*at_code += 1) >= SIZEOF(attrs_to_cycle))
2634             *at_code = 0;
2635         break;
2636     case 'V':
2637         if (*at_code == 0)
2638             *at_code = SIZEOF(attrs_to_cycle) - 1;
2639         else
2640             *at_code -= 1;
2641         break;
2642     default:
2643         result = FALSE;
2644         break;
2645     }
2646     if (result)
2647         *attr = attrs_to_cycle[*at_code].attr;
2648     return result;
2649 }
2650
2651 static bool
2652 cycle_colors(int ch, int *fg, int *bg, short *pair)
2653 {
2654     bool result = FALSE;
2655
2656     if (use_colors) {
2657         result = TRUE;
2658         switch (ch) {
2659         case 'F':
2660             if ((*fg -= 1) < 0)
2661                 *fg = COLORS - 1;
2662             break;
2663         case 'f':
2664             if ((*fg += 1) >= COLORS)
2665                 *fg = 0;
2666             break;
2667         case 'B':
2668             if ((*bg -= 1) < 0)
2669                 *bg = COLORS - 1;
2670             break;
2671         case 'b':
2672             if ((*bg += 1) >= COLORS)
2673                 *bg = 0;
2674             break;
2675         default:
2676             result = FALSE;
2677             break;
2678         }
2679         if (result) {
2680             *pair = (short) (*fg != COLOR_BLACK || *bg != COLOR_BLACK);
2681             if (*pair != 0) {
2682                 *pair = 1;
2683                 if (init_pair(*pair, (short) *fg, (short) *bg) == ERR) {
2684                     result = FALSE;
2685                 }
2686             }
2687         }
2688     }
2689     return result;
2690 }
2691
2692 /****************************************************************************
2693  *
2694  * Soft-key label test
2695  *
2696  ****************************************************************************/
2697
2698 #if USE_SOFTKEYS
2699
2700 #define SLK_HELP 17
2701 #define SLK_WORK (SLK_HELP + 3)
2702
2703 static void
2704 slk_help(void)
2705 {
2706     static const char *table[] =
2707     {
2708         "Available commands are:"
2709         ,""
2710         ,"^L         -- repaint this message and activate soft keys"
2711         ,"a/d        -- activate/disable soft keys"
2712         ,"c          -- set centered format for labels"
2713         ,"l          -- set left-justified format for labels"
2714         ,"r          -- set right-justified format for labels"
2715         ,"[12345678] -- set label; labels are numbered 1 through 8"
2716         ,"e          -- erase stdscr (should not erase labels)"
2717         ,"s          -- test scrolling of shortened screen"
2718         ,"v/V        -- cycle through video attributes"
2719 #if HAVE_SLK_COLOR
2720         ,"F/f/B/b    -- cycle through foreground/background colors"
2721 #endif
2722         ,"ESC        -- return to main menu"
2723         ,""
2724         ,"Note: if activating the soft keys causes your terminal to scroll up"
2725         ,"one line, your terminal auto-scrolls when anything is written to the"
2726         ,"last screen position.  The ncurses code does not yet handle this"
2727         ,"gracefully."
2728     };
2729     unsigned j;
2730
2731     move(2, 0);
2732     for (j = 0; j < SIZEOF(table); ++j) {
2733         P(table[j]);
2734     }
2735     refresh();
2736 }
2737
2738 #if HAVE_SLK_COLOR
2739 static void
2740 call_slk_color(int fg, int bg)
2741 {
2742     init_pair(1, (short) bg, (short) fg);
2743     slk_color(1);
2744     MvPrintw(SLK_WORK, 0, "Colors %d/%d\n", fg, bg);
2745     clrtoeol();
2746     slk_touch();
2747     slk_noutrefresh();
2748     refresh();
2749 }
2750 #endif
2751
2752 static void
2753 slk_test(void)
2754 /* exercise the soft keys */
2755 {
2756     int c, fmt = 1;
2757     char buf[9];
2758     char *s;
2759     chtype attr = A_NORMAL;
2760     unsigned at_code = 0;
2761 #if HAVE_SLK_COLOR
2762     int fg = COLOR_BLACK;
2763     int bg = COLOR_WHITE;
2764     short pair = 0;
2765 #endif
2766
2767     c = CTRL('l');
2768 #if HAVE_SLK_COLOR
2769     if (use_colors) {
2770         call_slk_color(fg, bg);
2771     }
2772 #endif
2773
2774     do {
2775         move(0, 0);
2776         switch (c) {
2777         case CTRL('l'):
2778             erase();
2779             attron(A_BOLD);
2780             MvAddStr(0, 20, "Soft Key Exerciser");
2781             attroff(A_BOLD);
2782
2783             slk_help();
2784             /* fall through */
2785
2786         case 'a':
2787             slk_restore();
2788             break;
2789
2790         case 'e':
2791             wclear(stdscr);
2792             break;
2793
2794         case 's':
2795             MvPrintw(SLK_WORK, 0, "Press Q to stop the scrolling-test: ");
2796             while ((c = Getchar()) != 'Q' && (c != ERR))
2797                 addch((chtype) c);
2798             break;
2799
2800         case 'd':
2801             slk_clear();
2802             break;
2803
2804         case 'l':
2805             fmt = 0;
2806             break;
2807
2808         case 'c':
2809             fmt = 1;
2810             break;
2811
2812         case 'r':
2813             fmt = 2;
2814             break;
2815
2816         case '1':
2817         case '2':
2818         case '3':
2819         case '4':
2820         case '5':
2821         case '6':
2822         case '7':
2823         case '8':
2824             MvAddStr(SLK_WORK, 0, "Please enter the label value: ");
2825             strcpy(buf, "");
2826             if ((s = slk_label(c - '0')) != 0) {
2827                 strncpy(buf, s, 8);
2828             }
2829             wGetstring(stdscr, buf, 8);
2830             slk_set((c - '0'), buf, fmt);
2831             slk_refresh();
2832             move(SLK_WORK, 0);
2833             clrtobot();
2834             break;
2835
2836         case case_QUIT:
2837             goto done;
2838
2839 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
2840         case KEY_RESIZE:
2841             wnoutrefresh(stdscr);
2842             break;
2843 #endif
2844
2845         default:
2846             if (cycle_attr(c, &at_code, &attr)) {
2847                 slk_attrset(attr);
2848                 slk_touch();
2849                 slk_noutrefresh();
2850                 break;
2851             }
2852 #if HAVE_SLK_COLOR
2853             if (cycle_colors(c, &fg, &bg, &pair)) {
2854                 if (use_colors) {
2855                     call_slk_color(fg, bg);
2856                 } else {
2857                     beep();
2858                 }
2859                 break;
2860             }
2861 #endif
2862             beep();
2863             break;
2864         }
2865     } while (!isQuit(c = Getchar()));
2866
2867   done:
2868     slk_clear();
2869     erase();
2870     endwin();
2871 }
2872
2873 #if USE_WIDEC_SUPPORT
2874 #define SLKLEN 8
2875 static void
2876 wide_slk_test(void)
2877 /* exercise the soft keys */
2878 {
2879     int c, fmt = 1;
2880     wchar_t buf[SLKLEN + 1];
2881     char *s;
2882     chtype attr = A_NORMAL;
2883     unsigned at_code = 0;
2884     int fg = COLOR_BLACK;
2885     int bg = COLOR_WHITE;
2886     short pair = 0;
2887
2888     c = CTRL('l');
2889     if (use_colors) {
2890         call_slk_color(fg, bg);
2891     }
2892     do {
2893         move(0, 0);
2894         switch (c) {
2895         case CTRL('l'):
2896             erase();
2897             attr_on(WA_BOLD, NULL);
2898             MvAddStr(0, 20, "Soft Key Exerciser");
2899             attr_off(WA_BOLD, NULL);
2900
2901             slk_help();
2902             /* fall through */
2903
2904         case 'a':
2905             slk_restore();
2906             break;
2907
2908         case 'e':
2909             wclear(stdscr);
2910             break;
2911
2912         case 's':
2913             MvPrintw(SLK_WORK, 0, "Press Q to stop the scrolling-test: ");
2914             while ((c = Getchar()) != 'Q' && (c != ERR))
2915                 addch((chtype) c);
2916             break;
2917
2918         case 'd':
2919             slk_clear();
2920             break;
2921
2922         case 'l':
2923             fmt = 0;
2924             break;
2925
2926         case 'c':
2927             fmt = 1;
2928             break;
2929
2930         case 'r':
2931             fmt = 2;
2932             break;
2933
2934         case '1':
2935         case '2':
2936         case '3':
2937         case '4':
2938         case '5':
2939         case '6':
2940         case '7':
2941         case '8':
2942             MvAddStr(SLK_WORK, 0, "Please enter the label value: ");
2943             *buf = 0;
2944             if ((s = slk_label(c - '0')) != 0) {
2945                 char *temp = strdup(s);
2946                 size_t used = strlen(temp);
2947                 size_t want = SLKLEN;
2948                 size_t test;
2949 #ifndef state_unused
2950                 mbstate_t state;
2951 #endif
2952
2953                 buf[0] = L'\0';
2954                 while (want > 0 && used != 0) {
2955                     const char *base = s;
2956                     reset_mbytes(state);
2957                     test = count_mbytes(base, 0, &state);
2958                     if (test == (size_t) -1) {
2959                         temp[--used] = 0;
2960                     } else if (test > want) {
2961                         temp[--used] = 0;
2962                     } else {
2963                         reset_mbytes(state);
2964                         trans_mbytes(buf, base, want, &state);
2965                         break;
2966                     }
2967                 }
2968                 free(temp);
2969             }
2970             wGet_wstring(stdscr, buf, SLKLEN);
2971             slk_wset((c - '0'), buf, fmt);
2972             slk_refresh();
2973             move(SLK_WORK, 0);
2974             clrtobot();
2975             break;
2976
2977         case case_QUIT:
2978             goto done;
2979
2980         case 'F':
2981             if (use_colors) {
2982                 fg = (short) ((fg + 1) % COLORS);
2983                 call_slk_color(fg, bg);
2984             }
2985             break;
2986         case 'B':
2987             if (use_colors) {
2988                 bg = (short) ((bg + 1) % COLORS);
2989                 call_slk_color(fg, bg);
2990             }
2991             break;
2992 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
2993         case KEY_RESIZE:
2994             wnoutrefresh(stdscr);
2995             break;
2996 #endif
2997         default:
2998             if (cycle_attr(c, &at_code, &attr)) {
2999                 slk_attr_set(attr, (short) (fg || bg), NULL);
3000                 slk_touch();
3001                 slk_noutrefresh();
3002                 break;
3003             }
3004 #if HAVE_SLK_COLOR
3005             if (cycle_colors(c, &fg, &bg, &pair)) {
3006                 if (use_colors) {
3007                     call_slk_color(fg, bg);
3008                 } else {
3009                     beep();
3010                 }
3011                 break;
3012             }
3013 #endif
3014             beep();
3015             break;
3016         }
3017     } while (!isQuit(c = Getchar()));
3018
3019   done:
3020     slk_clear();
3021     erase();
3022     endwin();
3023 }
3024 #endif
3025 #endif /* SLK_INIT */
3026
3027 static void
3028 show_256_chars(int repeat, attr_t attr, short pair)
3029 {
3030     unsigned first = 0;
3031     unsigned last = 255;
3032     unsigned code;
3033     int count;
3034
3035     erase();
3036     attron(A_BOLD);
3037     MvPrintw(0, 20, "Display of Character Codes %#0x to %#0x",
3038              first, last);
3039     attroff(A_BOLD);
3040     refresh();
3041
3042     for (code = first; code <= last; ++code) {
3043         int row = (int) (2 + (code / 16));
3044         int col = (int) (5 * (code % 16));
3045         mvaddch(row, col, colored_chtype(code, attr, pair));
3046         for (count = 1; count < repeat; ++count) {
3047             addch(colored_chtype(code, attr, pair));
3048         }
3049     }
3050
3051 }
3052
3053 /*
3054  * Show a slice of 32 characters, allowing those to be repeated up to the
3055  * screen's width.
3056  *
3057  * ISO 6429:  codes 0x80 to 0x9f may be control characters that cause the
3058  * terminal to perform functions.  The remaining codes can be graphic.
3059  */
3060 static void
3061 show_upper_chars(int base, int pagesize, int repeat, attr_t attr, short pair)
3062 {
3063     unsigned code;
3064     unsigned first = (unsigned) base;
3065     unsigned last = first + (unsigned) pagesize - 2;
3066     bool C1 = (first == 128);
3067     int reply;
3068
3069     erase();
3070     attron(A_BOLD);
3071     MvPrintw(0, 20, "Display of %s Character Codes %d to %d",
3072              C1 ? "C1" : "GR", first, last);
3073     attroff(A_BOLD);
3074     refresh();
3075
3076     for (code = first; code <= last; code++) {
3077         int count = repeat;
3078         int row = 2 + ((int) (code - first) % (pagesize / 2));
3079         int col = ((int) (code - first) / (pagesize / 2)) * COLS / 2;
3080         char tmp[80];
3081         sprintf(tmp, "%3u (0x%x)", code, code);
3082         MvPrintw(row, col, "%*s: ", COLS / 4, tmp);
3083
3084         do {
3085             if (C1)
3086                 nodelay(stdscr, TRUE);
3087             echochar(colored_chtype(code, attr, pair));
3088             if (C1) {
3089                 /* (yes, this _is_ crude) */
3090                 while ((reply = Getchar()) != ERR) {
3091                     addch(UChar(reply));
3092                     napms(10);
3093                 }
3094                 nodelay(stdscr, FALSE);
3095             }
3096         } while (--count > 0);
3097     }
3098 }
3099
3100 #define PC_COLS 4
3101
3102 static void
3103 show_pc_chars(int repeat, attr_t attr, short pair)
3104 {
3105     unsigned code;
3106
3107     erase();
3108     attron(A_BOLD);
3109     MvPrintw(0, 20, "Display of PC Character Codes");
3110     attroff(A_BOLD);
3111     refresh();
3112
3113     for (code = 0; code < 16; ++code) {
3114         MvPrintw(2, (int) code * PC_COLS + 8, "%X", code);
3115     }
3116     for (code = 0; code < 256; code++) {
3117         int count = repeat;
3118         int row = 3 + (int) (code / 16) + (code >= 128);
3119         int col = 8 + (int) (code % 16) * PC_COLS;
3120         if ((code % 16) == 0)
3121             MvPrintw(row, 0, "0x%02x:", code);
3122         move(row, col);
3123         do {
3124             switch (code) {
3125             case '\n':
3126             case '\r':
3127             case '\b':
3128             case '\f':
3129             case '\033':
3130             case 0x9b:
3131                 /*
3132                  * Skip the ones that do not work.
3133                  */
3134                 break;
3135             default:
3136                 addch(colored_chtype(code, A_ALTCHARSET | attr, pair));
3137                 break;
3138             }
3139         } while (--count > 0);
3140     }
3141 }
3142
3143 static void
3144 show_box_chars(int repeat, attr_t attr, short pair)
3145 {
3146     (void) repeat;
3147
3148     attr |= (attr_t) COLOR_PAIR(pair);
3149
3150     erase();
3151     attron(A_BOLD);
3152     MvAddStr(0, 20, "Display of the ACS Line-Drawing Set");
3153     attroff(A_BOLD);
3154     refresh();
3155     /* *INDENT-OFF* */
3156     wborder(stdscr,
3157             colored_chtype(ACS_VLINE,    attr, pair),
3158             colored_chtype(ACS_VLINE,    attr, pair),
3159             colored_chtype(ACS_HLINE,    attr, pair),
3160             colored_chtype(ACS_HLINE,    attr, pair),
3161             colored_chtype(ACS_ULCORNER, attr, pair),
3162             colored_chtype(ACS_URCORNER, attr, pair),
3163             colored_chtype(ACS_LLCORNER, attr, pair),
3164             colored_chtype(ACS_LRCORNER, attr, pair));
3165     MvHLine(LINES / 2, 0,        colored_chtype(ACS_HLINE, attr, pair), COLS);
3166     MvVLine(0,         COLS / 2, colored_chtype(ACS_VLINE, attr, pair), LINES);
3167     MvAddCh(0,         COLS / 2, colored_chtype(ACS_TTEE,  attr, pair));
3168     MvAddCh(LINES / 2, COLS / 2, colored_chtype(ACS_PLUS,  attr, pair));
3169     MvAddCh(LINES - 1, COLS / 2, colored_chtype(ACS_BTEE,  attr, pair));
3170     MvAddCh(LINES / 2, 0,        colored_chtype(ACS_LTEE,  attr, pair));
3171     MvAddCh(LINES / 2, COLS - 1, colored_chtype(ACS_RTEE,  attr, pair));
3172     /* *INDENT-ON* */
3173
3174 }
3175
3176 static int
3177 show_1_acs(int n, int repeat, const char *name, chtype code)
3178 {
3179     const int height = 16;
3180     int row = 2 + (n % height);
3181     int col = (n / height) * COLS / 2;
3182
3183     MvPrintw(row, col, "%*s : ", COLS / 4, name);
3184     do {
3185         addch(code);
3186     } while (--repeat > 0);
3187     return n + 1;
3188 }
3189
3190 static void
3191 show_acs_chars(int repeat, attr_t attr, short pair)
3192 /* display the ACS character set */
3193 {
3194     int n;
3195
3196 #define BOTH(name) #name, colored_chtype(name, attr, (chtype) pair)
3197
3198     erase();
3199     attron(A_BOLD);
3200     MvAddStr(0, 20, "Display of the ACS Character Set");
3201     attroff(A_BOLD);
3202     refresh();
3203
3204     n = show_1_acs(0, repeat, BOTH(ACS_ULCORNER));
3205     n = show_1_acs(n, repeat, BOTH(ACS_URCORNER));
3206     n = show_1_acs(n, repeat, BOTH(ACS_LLCORNER));
3207     n = show_1_acs(n, repeat, BOTH(ACS_LRCORNER));
3208
3209     n = show_1_acs(n, repeat, BOTH(ACS_LTEE));
3210     n = show_1_acs(n, repeat, BOTH(ACS_RTEE));
3211     n = show_1_acs(n, repeat, BOTH(ACS_TTEE));
3212     n = show_1_acs(n, repeat, BOTH(ACS_BTEE));
3213
3214     n = show_1_acs(n, repeat, BOTH(ACS_HLINE));
3215     n = show_1_acs(n, repeat, BOTH(ACS_VLINE));
3216
3217     /*
3218      * HPUX's ACS definitions are broken here.  Just give up.
3219      */
3220 #if !(defined(__hpux) && !defined(NCURSES_VERSION))
3221     n = show_1_acs(n, repeat, BOTH(ACS_LARROW));
3222     n = show_1_acs(n, repeat, BOTH(ACS_RARROW));
3223     n = show_1_acs(n, repeat, BOTH(ACS_UARROW));
3224     n = show_1_acs(n, repeat, BOTH(ACS_DARROW));
3225
3226     n = show_1_acs(n, repeat, BOTH(ACS_BLOCK));
3227     n = show_1_acs(n, repeat, BOTH(ACS_BOARD));
3228     n = show_1_acs(n, repeat, BOTH(ACS_LANTERN));
3229     n = show_1_acs(n, repeat, BOTH(ACS_BULLET));
3230     n = show_1_acs(n, repeat, BOTH(ACS_CKBOARD));
3231     n = show_1_acs(n, repeat, BOTH(ACS_DEGREE));
3232     n = show_1_acs(n, repeat, BOTH(ACS_DIAMOND));
3233     n = show_1_acs(n, repeat, BOTH(ACS_PLMINUS));
3234     n = show_1_acs(n, repeat, BOTH(ACS_PLUS));
3235
3236     n = show_1_acs(n, repeat, BOTH(ACS_GEQUAL));
3237     n = show_1_acs(n, repeat, BOTH(ACS_NEQUAL));
3238     n = show_1_acs(n, repeat, BOTH(ACS_LEQUAL));
3239
3240     n = show_1_acs(n, repeat, BOTH(ACS_STERLING));
3241     n = show_1_acs(n, repeat, BOTH(ACS_PI));
3242     n = show_1_acs(n, repeat, BOTH(ACS_S1));
3243     n = show_1_acs(n, repeat, BOTH(ACS_S3));
3244     n = show_1_acs(n, repeat, BOTH(ACS_S7));
3245     (void) show_1_acs(n, repeat, BOTH(ACS_S9));
3246 #endif
3247 }
3248
3249 static void
3250 acs_display(void)
3251 {
3252     int c = 'a';
3253     int pagesize = 32;
3254     char *term = getenv("TERM");
3255     const char *pch_kludge = ((term != 0 && strstr(term, "linux"))
3256                               ? "p=PC, "
3257                               : "");
3258     chtype attr = A_NORMAL;
3259     int digit = 0;
3260     int repeat = 1;
3261     int fg = COLOR_BLACK;
3262     int bg = COLOR_BLACK;
3263     unsigned at_code = 0;
3264     short pair = 0;
3265     void (*last_show_acs) (int, attr_t, short) = 0;
3266
3267     do {
3268         switch (c) {
3269         case CTRL('L'):
3270             Repaint();
3271             break;
3272         case 'a':
3273             ToggleAcs(last_show_acs, show_acs_chars);
3274             break;
3275         case 'p':
3276             if (*pch_kludge)
3277                 ToggleAcs(last_show_acs, show_pc_chars);
3278             else
3279                 beep();
3280             break;
3281         case 'w':
3282             if (pagesize == 32) {
3283                 pagesize = 256;
3284             } else {
3285                 pagesize = 32;
3286             }
3287             break;
3288         case 'x':
3289             ToggleAcs(last_show_acs, show_box_chars);
3290             break;
3291         case '0':
3292         case '1':
3293         case '2':
3294         case '3':
3295             digit = (c - '0');
3296             last_show_acs = 0;
3297             break;
3298         case '-':
3299             if (digit > 0) {
3300                 --digit;
3301                 last_show_acs = 0;
3302             } else {
3303                 beep();
3304             }
3305             break;
3306         case '+':
3307             if (digit < 3) {
3308                 ++digit;
3309                 last_show_acs = 0;
3310             } else {
3311                 beep();
3312             }
3313             break;
3314         case '>':
3315             if (repeat < (COLS / 4))
3316                 ++repeat;
3317             break;
3318         case '<':
3319             if (repeat > 1)
3320                 --repeat;
3321             break;
3322         default:
3323             if (cycle_attr(c, &at_code, &attr)
3324                 || cycle_colors(c, &fg, &bg, &pair)) {
3325                 break;
3326             } else {
3327                 beep();
3328             }
3329             break;
3330         }
3331         if (pagesize != 32) {
3332             show_256_chars(repeat, attr, pair);
3333         } else if (last_show_acs != 0) {
3334             last_show_acs(repeat, attr, pair);
3335         } else {
3336             show_upper_chars(digit * pagesize + 128, pagesize, repeat, attr, pair);
3337         }
3338
3339         MvPrintw(LINES - 3, 0,
3340                  "Note: ANSI terminals may not display C1 characters.");
3341         MvPrintw(LINES - 2, 0,
3342                  "Select: a=ACS, w=all x=box, %s0=C1, 1-3,+/- non-ASCII, </> repeat, ESC=quit",
3343                  pch_kludge);
3344         if (use_colors) {
3345             MvPrintw(LINES - 1, 0,
3346                      "v/V, f/F, b/B cycle through video attributes (%s) and color %d/%d.",
3347                      attrs_to_cycle[at_code].name,
3348                      fg, bg);
3349         } else {
3350             MvPrintw(LINES - 1, 0,
3351                      "v/V cycles through video attributes (%s).",
3352                      attrs_to_cycle[at_code].name);
3353         }
3354         refresh();
3355     } while (!isQuit(c = Getchar()));
3356
3357     Pause();
3358     erase();
3359     endwin();
3360 }
3361
3362 #if USE_WIDEC_SUPPORT
3363 static cchar_t *
3364 merge_wide_attr(cchar_t *dst, const cchar_t *src, attr_t attr, short pair)
3365 {
3366     int count;
3367
3368     *dst = *src;
3369     do {
3370         TEST_CCHAR(src, count, {
3371             attr |= (test_attrs & A_ALTCHARSET);
3372             setcchar(dst, test_wch, attr, pair, NULL);
3373         }
3374         , {
3375             ;
3376         });
3377     } while (0);
3378     return dst;
3379 }
3380
3381 /*
3382  * Header/legend take up no more than 8 lines, leaving 16 lines on a 24-line
3383  * display.  If there are no repeats, we could normally display 16 lines of 64
3384  * characters (1024 total).  However, taking repeats and double-width cells
3385  * into account, use 256 characters for the page.
3386  */
3387 static void
3388 show_paged_widechars(int base,
3389                      int pagesize,
3390                      int repeat,
3391                      int space,
3392                      attr_t attr,
3393                      short pair)
3394 {
3395     int first = base * pagesize;
3396     int last = first + pagesize - 1;
3397     int per_line = 16;
3398     cchar_t temp;
3399     wchar_t code;
3400     wchar_t codes[10];
3401
3402     erase();
3403     attron(A_BOLD);
3404     MvPrintw(0, 20, "Display of Character Codes %#x to %#x", first, last);
3405     attroff(A_BOLD);
3406
3407     for (code = first; (int) code <= last; code++) {
3408         int row = (2 + ((int) code - first) / per_line);
3409         int col = 5 * ((int) code % per_line);
3410         int count;
3411
3412         memset(&codes, 0, sizeof(codes));
3413         codes[0] = code;
3414         setcchar(&temp, codes, attr, pair, 0);
3415         move(row, col);
3416         if (wcwidth(code) == 0 && code != 0) {
3417             addch((chtype) space |
3418                   (A_REVERSE ^ attr) |
3419                   (attr_t) COLOR_PAIR(pair));
3420         }
3421         add_wch(&temp);
3422         for (count = 1; count < repeat; ++count) {
3423             add_wch(&temp);
3424         }
3425     }
3426 }
3427
3428 static void
3429 show_upper_widechars(int first, int repeat, int space, attr_t attr, short pair)
3430 {
3431     cchar_t temp;
3432     wchar_t code;
3433     int last = first + 31;
3434
3435     erase();
3436     attron(A_BOLD);
3437     MvPrintw(0, 20, "Display of Character Codes %d to %d", first, last);
3438     attroff(A_BOLD);
3439
3440     for (code = first; (int) code <= last; code++) {
3441         int row = 2 + ((code - first) % 16);
3442         int col = ((code - first) / 16) * COLS / 2;
3443         wchar_t codes[10];
3444         char tmp[80];
3445         int count = repeat;
3446         int y, x;
3447
3448         sprintf(tmp, "%3ld (0x%lx)", (long) code, (long) code);
3449         MvPrintw(row, col, "%*s: ", COLS / 4, tmp);
3450
3451         memset(&codes, 0, sizeof(codes));
3452         codes[0] = code;
3453         setcchar(&temp, codes, attr, pair, 0);
3454
3455         do {
3456             /*
3457              * Give non-spacing characters something to combine with.  If we
3458              * don't, they'll bunch up in a heap on the space after the ":".
3459              * Mark them with reverse-video to make them simpler to find on
3460              * the display.
3461              */
3462             if (wcwidth(code) == 0) {
3463                 addch((chtype) space |
3464                       (A_REVERSE ^ attr) |
3465                       (attr_t) COLOR_PAIR(pair));
3466             }
3467             /*
3468              * This uses echo_wchar(), for comparison with the normal 'f'
3469              * test (and to make a test-case for echo_wchar()).  The screen
3470              * may flicker because the erase() at the top of the function
3471              * is met by the builtin refresh() in echo_wchar().
3472              */
3473             echo_wchar(&temp);
3474             /*
3475              * The repeat-count may make text wrap - avoid that.
3476              */
3477             getyx(stdscr, y, x);
3478             (void) y;
3479             if (x >= col + (COLS / 2) - 2)
3480                 break;
3481         } while (--count > 0);
3482     }
3483 }
3484
3485 static int
3486 show_1_wacs(int n, int repeat, const char *name, const cchar_t *code)
3487 {
3488     const int height = 16;
3489     int row = 2 + (n % height);
3490     int col = (n / height) * COLS / 2;
3491
3492     MvPrintw(row, col, "%*s : ", COLS / 4, name);
3493     while (--repeat >= 0) {
3494         add_wch(code);
3495     }
3496     return n + 1;
3497 }
3498
3499 #define MERGE_ATTR(wch) merge_wide_attr(&temp, wch, attr, pair)
3500
3501 static void
3502 show_wacs_chars(int repeat, attr_t attr, short pair)
3503 /* display the wide-ACS character set */
3504 {
3505     cchar_t temp;
3506
3507     int n;
3508
3509 /*#define BOTH2(name) #name, &(name) */
3510 #define BOTH2(name) #name, MERGE_ATTR(name)
3511
3512     erase();
3513     attron(A_BOLD);
3514     MvAddStr(0, 20, "Display of the Wide-ACS Character Set");
3515     attroff(A_BOLD);
3516     refresh();
3517
3518     n = show_1_wacs(0, repeat, BOTH2(WACS_ULCORNER));
3519     n = show_1_wacs(n, repeat, BOTH2(WACS_URCORNER));
3520     n = show_1_wacs(n, repeat, BOTH2(WACS_LLCORNER));
3521     n = show_1_wacs(n, repeat, BOTH2(WACS_LRCORNER));
3522
3523     n = show_1_wacs(n, repeat, BOTH2(WACS_LTEE));
3524     n = show_1_wacs(n, repeat, BOTH2(WACS_RTEE));
3525     n = show_1_wacs(n, repeat, BOTH2(WACS_TTEE));
3526     n = show_1_wacs(n, repeat, BOTH2(WACS_BTEE));
3527
3528     n = show_1_wacs(n, repeat, BOTH2(WACS_HLINE));
3529     n = show_1_wacs(n, repeat, BOTH2(WACS_VLINE));
3530
3531     n = show_1_wacs(n, repeat, BOTH2(WACS_LARROW));
3532     n = show_1_wacs(n, repeat, BOTH2(WACS_RARROW));
3533     n = show_1_wacs(n, repeat, BOTH2(WACS_UARROW));
3534     n = show_1_wacs(n, repeat, BOTH2(WACS_DARROW));
3535
3536     n = show_1_wacs(n, repeat, BOTH2(WACS_BLOCK));
3537     n = show_1_wacs(n, repeat, BOTH2(WACS_BOARD));
3538     n = show_1_wacs(n, repeat, BOTH2(WACS_LANTERN));
3539     n = show_1_wacs(n, repeat, BOTH2(WACS_BULLET));
3540     n = show_1_wacs(n, repeat, BOTH2(WACS_CKBOARD));
3541     n = show_1_wacs(n, repeat, BOTH2(WACS_DEGREE));
3542     n = show_1_wacs(n, repeat, BOTH2(WACS_DIAMOND));
3543     n = show_1_wacs(n, repeat, BOTH2(WACS_PLMINUS));
3544     n = show_1_wacs(n, repeat, BOTH2(WACS_PLUS));
3545
3546 #ifdef CURSES_WACS_ARRAY
3547     n = show_1_wacs(n, repeat, BOTH2(WACS_GEQUAL));
3548     n = show_1_wacs(n, repeat, BOTH2(WACS_NEQUAL));
3549     n = show_1_wacs(n, repeat, BOTH2(WACS_LEQUAL));
3550
3551     n = show_1_wacs(n, repeat, BOTH2(WACS_STERLING));
3552     n = show_1_wacs(n, repeat, BOTH2(WACS_PI));
3553     n = show_1_wacs(n, repeat, BOTH2(WACS_S1));
3554     n = show_1_wacs(n, repeat, BOTH2(WACS_S3));
3555     n = show_1_wacs(n, repeat, BOTH2(WACS_S7));
3556     (void) show_1_wacs(n, repeat, BOTH2(WACS_S9));
3557 #endif
3558 }
3559
3560 #ifdef WACS_D_PLUS
3561 static void
3562 show_wacs_chars_double(int repeat, attr_t attr, short pair)
3563 /* display the wide-ACS character set */
3564 {
3565     cchar_t temp;
3566
3567     int n;
3568
3569 /*#define BOTH2(name) #name, &(name) */
3570 #define BOTH2(name) #name, MERGE_ATTR(name)
3571
3572     erase();
3573     attron(A_BOLD);
3574     MvAddStr(0, 20, "Display of the Wide-ACS Character Set");
3575     attroff(A_BOLD);
3576     refresh();
3577
3578     n = show_1_wacs(0, repeat, BOTH2(WACS_D_ULCORNER));
3579     n = show_1_wacs(n, repeat, BOTH2(WACS_D_URCORNER));
3580     n = show_1_wacs(n, repeat, BOTH2(WACS_D_LLCORNER));
3581     n = show_1_wacs(n, repeat, BOTH2(WACS_D_LRCORNER));
3582
3583     n = show_1_wacs(n, repeat, BOTH2(WACS_D_LTEE));
3584     n = show_1_wacs(n, repeat, BOTH2(WACS_D_RTEE));
3585     n = show_1_wacs(n, repeat, BOTH2(WACS_D_TTEE));
3586     n = show_1_wacs(n, repeat, BOTH2(WACS_D_BTEE));
3587
3588     n = show_1_wacs(n, repeat, BOTH2(WACS_D_HLINE));
3589     n = show_1_wacs(n, repeat, BOTH2(WACS_D_VLINE));
3590
3591     n = show_1_wacs(n, repeat, BOTH2(WACS_LARROW));
3592     n = show_1_wacs(n, repeat, BOTH2(WACS_RARROW));
3593     n = show_1_wacs(n, repeat, BOTH2(WACS_UARROW));
3594     n = show_1_wacs(n, repeat, BOTH2(WACS_DARROW));
3595
3596     n = show_1_wacs(n, repeat, BOTH2(WACS_BLOCK));
3597     n = show_1_wacs(n, repeat, BOTH2(WACS_BOARD));
3598     n = show_1_wacs(n, repeat, BOTH2(WACS_LANTERN));
3599     n = show_1_wacs(n, repeat, BOTH2(WACS_BULLET));
3600     n = show_1_wacs(n, repeat, BOTH2(WACS_CKBOARD));
3601     n = show_1_wacs(n, repeat, BOTH2(WACS_DEGREE));
3602     n = show_1_wacs(n, repeat, BOTH2(WACS_DIAMOND));
3603     n = show_1_wacs(n, repeat, BOTH2(WACS_PLMINUS));
3604     n = show_1_wacs(n, repeat, BOTH2(WACS_PLUS));
3605
3606 #ifdef CURSES_WACS_ARRAY
3607     n = show_1_wacs(n, repeat, BOTH2(WACS_GEQUAL));
3608     n = show_1_wacs(n, repeat, BOTH2(WACS_NEQUAL));
3609     n = show_1_wacs(n, repeat, BOTH2(WACS_LEQUAL));
3610
3611     n = show_1_wacs(n, repeat, BOTH2(WACS_STERLING));
3612     n = show_1_wacs(n, repeat, BOTH2(WACS_PI));
3613     n = show_1_wacs(n, repeat, BOTH2(WACS_S1));
3614     n = show_1_wacs(n, repeat, BOTH2(WACS_S3));
3615     n = show_1_wacs(n, repeat, BOTH2(WACS_S7));
3616     (void) show_1_wacs(n, repeat, BOTH2(WACS_S9));
3617 #endif
3618 }
3619 #endif
3620
3621 #ifdef WACS_T_PLUS
3622 static void
3623 show_wacs_chars_thick(int repeat, attr_t attr, short pair)
3624 /* display the wide-ACS character set */
3625 {
3626     cchar_t temp;
3627
3628     int n;
3629
3630 /*#define BOTH2(name) #name, &(name) */
3631 #define BOTH2(name) #name, MERGE_ATTR(name)
3632
3633     erase();
3634     attron(A_BOLD);
3635     MvAddStr(0, 20, "Display of the Wide-ACS Character Set");
3636     attroff(A_BOLD);
3637     refresh();
3638
3639     n = show_1_wacs(0, repeat, BOTH2(WACS_T_ULCORNER));
3640     n = show_1_wacs(n, repeat, BOTH2(WACS_T_URCORNER));
3641     n = show_1_wacs(n, repeat, BOTH2(WACS_T_LLCORNER));
3642     n = show_1_wacs(n, repeat, BOTH2(WACS_T_LRCORNER));
3643
3644     n = show_1_wacs(n, repeat, BOTH2(WACS_T_LTEE));
3645     n = show_1_wacs(n, repeat, BOTH2(WACS_T_RTEE));
3646     n = show_1_wacs(n, repeat, BOTH2(WACS_T_TTEE));
3647     n = show_1_wacs(n, repeat, BOTH2(WACS_T_BTEE));
3648
3649     n = show_1_wacs(n, repeat, BOTH2(WACS_T_HLINE));
3650     n = show_1_wacs(n, repeat, BOTH2(WACS_T_VLINE));
3651
3652     n = show_1_wacs(n, repeat, BOTH2(WACS_LARROW));
3653     n = show_1_wacs(n, repeat, BOTH2(WACS_RARROW));
3654     n = show_1_wacs(n, repeat, BOTH2(WACS_UARROW));
3655     n = show_1_wacs(n, repeat, BOTH2(WACS_DARROW));
3656
3657     n = show_1_wacs(n, repeat, BOTH2(WACS_BLOCK));
3658     n = show_1_wacs(n, repeat, BOTH2(WACS_BOARD));
3659     n = show_1_wacs(n, repeat, BOTH2(WACS_LANTERN));
3660     n = show_1_wacs(n, repeat, BOTH2(WACS_BULLET));
3661     n = show_1_wacs(n, repeat, BOTH2(WACS_CKBOARD));
3662     n = show_1_wacs(n, repeat, BOTH2(WACS_DEGREE));
3663     n = show_1_wacs(n, repeat, BOTH2(WACS_DIAMOND));
3664     n = show_1_wacs(n, repeat, BOTH2(WACS_PLMINUS));
3665     n = show_1_wacs(n, repeat, BOTH2(WACS_PLUS));
3666
3667 #ifdef CURSES_WACS_ARRAY
3668     n = show_1_wacs(n, repeat, BOTH2(WACS_GEQUAL));
3669     n = show_1_wacs(n, repeat, BOTH2(WACS_NEQUAL));
3670     n = show_1_wacs(n, repeat, BOTH2(WACS_LEQUAL));
3671
3672     n = show_1_wacs(n, repeat, BOTH2(WACS_STERLING));
3673     n = show_1_wacs(n, repeat, BOTH2(WACS_PI));
3674     n = show_1_wacs(n, repeat, BOTH2(WACS_S1));
3675     n = show_1_wacs(n, repeat, BOTH2(WACS_S3));
3676     n = show_1_wacs(n, repeat, BOTH2(WACS_S7));
3677     (void) show_1_wacs(n, repeat, BOTH2(WACS_S9));
3678 #endif
3679 }
3680 #endif
3681
3682 #undef MERGE_ATTR
3683
3684 #define MERGE_ATTR(n,wch) merge_wide_attr(&temp[n], wch, attr, pair)
3685
3686 static void
3687 show_wbox_chars(int repeat, attr_t attr, short pair)
3688 {
3689     cchar_t temp[8];
3690
3691     (void) repeat;
3692     erase();
3693     attron(A_BOLD);
3694     MvAddStr(0, 20, "Display of the Wide-ACS Line-Drawing Set");
3695     attroff(A_BOLD);
3696     refresh();
3697
3698     wborder_set(stdscr,
3699                 MERGE_ATTR(0, WACS_VLINE),
3700                 MERGE_ATTR(1, WACS_VLINE),
3701                 MERGE_ATTR(2, WACS_HLINE),
3702                 MERGE_ATTR(3, WACS_HLINE),
3703                 MERGE_ATTR(4, WACS_ULCORNER),
3704                 MERGE_ATTR(5, WACS_URCORNER),
3705                 MERGE_ATTR(6, WACS_LLCORNER),
3706                 MERGE_ATTR(7, WACS_LRCORNER));
3707     /* *INDENT-OFF* */
3708     (void) mvhline_set(LINES / 2, 0,        MERGE_ATTR(0, WACS_HLINE), COLS);
3709     (void) mvvline_set(0,         COLS / 2, MERGE_ATTR(0, WACS_VLINE), LINES);
3710     (void) mvadd_wch(0,           COLS / 2, MERGE_ATTR(0, WACS_TTEE));
3711     (void) mvadd_wch(LINES / 2,   COLS / 2, MERGE_ATTR(0, WACS_PLUS));
3712     (void) mvadd_wch(LINES - 1,   COLS / 2, MERGE_ATTR(0, WACS_BTEE));
3713     (void) mvadd_wch(LINES / 2,   0,        MERGE_ATTR(0, WACS_LTEE));
3714     (void) mvadd_wch(LINES / 2,   COLS - 1, MERGE_ATTR(0, WACS_RTEE));
3715     /* *INDENT-ON* */
3716
3717 }
3718
3719 #undef MERGE_ATTR
3720
3721 static int
3722 show_2_wacs(int n, const char *name, const char *code, attr_t attr, short pair)
3723 {
3724     const int height = 16;
3725     int row = 2 + (n % height);
3726     int col = (n / height) * COLS / 2;
3727     char temp[80];
3728
3729     MvPrintw(row, col, "%*s : ", COLS / 4, name);
3730     (void) attr_set(attr, pair, 0);
3731     addstr(strncpy(temp, code, 20));
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 |= (chtype) COLOR_PAIR(1);
3912         }
3913         bkgdset(normal);
3914         erase();
3915         MvPrintw(1, 20, "Graphic rendition test pattern:");
3916
3917         MvPrintw(4, 1, "vanilla");
3918
3919 #define set_sgr(mask) bkgdset((normal^(mask)));
3920         set_sgr(A_BOLD);
3921         MvPrintw(4, 40, "bold");
3922
3923         set_sgr(A_UNDERLINE);
3924         MvPrintw(6, 6, "underline");
3925
3926         set_sgr(A_BOLD | A_UNDERLINE);
3927         MvPrintw(6, 45, "bold underline");
3928
3929         set_sgr(A_BLINK);
3930         MvPrintw(8, 1, "blink");
3931
3932         set_sgr(A_BLINK | A_BOLD);
3933         MvPrintw(8, 40, "bold blink");
3934
3935         set_sgr(A_UNDERLINE | A_BLINK);
3936         MvPrintw(10, 6, "underline blink");
3937
3938         set_sgr(A_BOLD | A_UNDERLINE | A_BLINK);
3939         MvPrintw(10, 45, "bold underline blink");
3940
3941         set_sgr(A_REVERSE);
3942         MvPrintw(12, 1, "negative");
3943
3944         set_sgr(A_BOLD | A_REVERSE);
3945         MvPrintw(12, 40, "bold negative");
3946
3947         set_sgr(A_UNDERLINE | A_REVERSE);
3948         MvPrintw(14, 6, "underline negative");
3949
3950         set_sgr(A_BOLD | A_UNDERLINE | A_REVERSE);
3951         MvPrintw(14, 45, "bold underline negative");
3952
3953         set_sgr(A_BLINK | A_REVERSE);
3954         MvPrintw(16, 1, "blink negative");
3955
3956         set_sgr(A_BOLD | A_BLINK | A_REVERSE);
3957         MvPrintw(16, 40, "bold blink negative");
3958
3959         set_sgr(A_UNDERLINE | A_BLINK | A_REVERSE);
3960         MvPrintw(18, 6, "underline blink negative");
3961
3962         set_sgr(A_BOLD | A_UNDERLINE | A_BLINK | A_REVERSE);
3963         MvPrintw(18, 45, "bold underline blink negative");
3964
3965         bkgdset(normal);
3966         MvPrintw(LINES - 2, 1, "%s background. ", pass == 0 ? "Dark" :
3967                  "Light");
3968         clrtoeol();
3969         Pause();
3970     }
3971
3972     bkgdset(A_NORMAL | BLANK);
3973     erase();
3974     endwin();
3975 }
3976
3977 /****************************************************************************
3978  *
3979  * Windows and scrolling tester.
3980  *
3981  ****************************************************************************/
3982
3983 #define BOTLINES        4       /* number of line stolen from screen bottom */
3984
3985 typedef struct {
3986     int y, x;
3987 } pair;
3988
3989 #define FRAME struct frame
3990 FRAME
3991 {
3992     FRAME *next, *last;
3993     bool do_scroll;
3994     bool do_keypad;
3995     WINDOW *wind;
3996 };
3997
3998 #if defined(NCURSES_VERSION)
3999 #if (NCURSES_VERSION_PATCH < 20070331) && NCURSES_EXT_FUNCS
4000 #define is_keypad(win)   (win)->_use_keypad
4001 #define is_scrollok(win) (win)->_scroll
4002 #elif !defined(is_keypad)
4003 #define is_keypad(win)   FALSE
4004 #define is_scrollok(win) FALSE
4005 #endif
4006 #else
4007 #define is_keypad(win)   FALSE
4008 #define is_scrollok(win) FALSE
4009 #endif
4010
4011 static WINDOW *
4012 frame_win(FRAME * curp)
4013 {
4014     return (curp != 0) ? curp->wind : stdscr;
4015 }
4016
4017 /* We need to know if these flags are actually set, so don't look in FRAME.
4018  * These names are known to work with SVr4 curses as well as ncurses.  The
4019  * _use_keypad name does not work with Solaris 8.
4020  */
4021 static bool
4022 HaveKeypad(FRAME * curp)
4023 {
4024     WINDOW *win = frame_win(curp);
4025     (void) win;
4026     return is_keypad(win);
4027 }
4028
4029 static bool
4030 HaveScroll(FRAME * curp)
4031 {
4032     WINDOW *win = frame_win(curp);
4033     (void) win;
4034     return is_scrollok(win);
4035 }
4036
4037 static void
4038 newwin_legend(FRAME * curp)
4039 {
4040     static const struct {
4041         const char *msg;
4042         int code;
4043     } legend[] = {
4044         {
4045             "^C = create window", 0
4046         },
4047         {
4048             "^N = next window", 0
4049         },
4050         {
4051             "^P = previous window", 0
4052         },
4053         {
4054             "^F = scroll forward", 0
4055         },
4056         {
4057             "^B = scroll backward", 0
4058         },
4059         {
4060             "^K = keypad(%s)", 1
4061         },
4062         {
4063             "^S = scrollok(%s)", 2
4064         },
4065         {
4066             "^W = save window to file", 0
4067         },
4068         {
4069             "^R = restore window", 0
4070         },
4071 #if HAVE_WRESIZE
4072         {
4073             "^X = resize", 0
4074         },
4075 #endif
4076         {
4077             "^Q%s = exit", 3
4078         }
4079     };
4080     size_t n;
4081     int x;
4082     bool do_keypad = HaveKeypad(curp);
4083     bool do_scroll = HaveScroll(curp);
4084     char buf[BUFSIZ];
4085
4086     move(LINES - 4, 0);
4087     for (n = 0; n < SIZEOF(legend); n++) {
4088         switch (legend[n].code) {
4089         default:
4090             strcpy(buf, legend[n].msg);
4091             break;
4092         case 1:
4093             sprintf(buf, legend[n].msg, do_keypad ? "yes" : "no");
4094             break;
4095         case 2:
4096             sprintf(buf, legend[n].msg, do_scroll ? "yes" : "no");
4097             break;
4098         case 3:
4099             sprintf(buf, legend[n].msg, do_keypad ? "/ESC" : "");
4100             break;
4101         }
4102         x = getcurx(stdscr);
4103         addstr((COLS < (x + 3 + (int) strlen(buf))) ? "\n" : (n ? ", " : ""));
4104         addstr(buf);
4105     }
4106     clrtoeol();
4107 }
4108
4109 static void
4110 transient(FRAME * curp, NCURSES_CONST char *msg)
4111 {
4112     newwin_legend(curp);
4113     if (msg) {
4114         MvAddStr(LINES - 1, 0, msg);
4115         refresh();
4116         napms(1000);
4117     }
4118
4119     move(LINES - 1, 0);
4120     printw("%s characters are echoed, window should %sscroll.",
4121            HaveKeypad(curp) ? "Non-arrow" : "All other",
4122            HaveScroll(curp) ? "" : "not ");
4123     clrtoeol();
4124 }
4125
4126 static void
4127 newwin_report(FRAME * curp)
4128 /* report on the cursor's current position, then restore it */
4129 {
4130     WINDOW *win = frame_win(curp);
4131     int y, x;
4132
4133     if (win != stdscr)
4134         transient(curp, (char *) 0);
4135     getyx(win, y, x);
4136     move(LINES - 1, COLS - 17);
4137     printw("Y = %2d X = %2d", y, x);
4138     if (win != stdscr)
4139         refresh();
4140     else
4141         wmove(win, y, x);
4142 }
4143
4144 static pair *
4145 selectcell(int uli, int ulj, int lri, int lrj)
4146 /* arrows keys move cursor, return location at current on non-arrow key */
4147 {
4148     static pair res;            /* result cell */
4149     int si = lri - uli + 1;     /* depth of the select area */
4150     int sj = lrj - ulj + 1;     /* width of the select area */
4151     int i = 0, j = 0;           /* offsets into the select area */
4152
4153     res.y = uli;
4154     res.x = ulj;
4155     for (;;) {
4156         move(uli + i, ulj + j);
4157         newwin_report((FRAME *) 0);
4158
4159         switch (Getchar()) {
4160         case KEY_UP:
4161             i += si - 1;
4162             break;
4163         case KEY_DOWN:
4164             i++;
4165             break;
4166         case KEY_LEFT:
4167             j += sj - 1;
4168             break;
4169         case KEY_RIGHT:
4170             j++;
4171             break;
4172         case case_QUIT:
4173             return ((pair *) 0);
4174 #ifdef NCURSES_MOUSE_VERSION
4175         case KEY_MOUSE:
4176             {
4177                 MEVENT event;
4178
4179                 getmouse(&event);
4180                 if (event.y > uli && event.x > ulj) {
4181                     i = event.y - uli;
4182                     j = event.x - ulj;
4183                 } else {
4184                     beep();
4185                     break;
4186                 }
4187             }
4188             /* FALLTHRU */
4189 #endif
4190         default:
4191             res.y = uli + i;
4192             res.x = ulj + j;
4193             return (&res);
4194         }
4195         i %= si;
4196         j %= sj;
4197     }
4198 }
4199
4200 static void
4201 outerbox(pair ul, pair lr, bool onoff)
4202 /* draw or erase a box *outside* the given pair of corners */
4203 {
4204     MvAddCh(ul.y - 1, lr.x - 1, onoff ? ACS_ULCORNER : ' ');
4205     MvAddCh(ul.y - 1, lr.x + 1, onoff ? ACS_URCORNER : ' ');
4206     MvAddCh(lr.y + 1, lr.x + 1, onoff ? ACS_LRCORNER : ' ');
4207     MvAddCh(lr.y + 1, ul.x - 1, onoff ? ACS_LLCORNER : ' ');
4208     move(ul.y - 1, ul.x);
4209     hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
4210     move(ul.y, ul.x - 1);
4211     vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
4212     move(lr.y + 1, ul.x);
4213     hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
4214     move(ul.y, lr.x + 1);
4215     vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
4216 }
4217
4218 static WINDOW *
4219 getwindow(void)
4220 /* Ask user for a window definition */
4221 {
4222     WINDOW *rwindow;
4223     pair ul, lr, *tmp;
4224
4225     move(0, 0);
4226     clrtoeol();
4227     addstr("Use arrows to move cursor, anything else to mark corner 1");
4228     refresh();
4229     if ((tmp = selectcell(2, 1, LINES - BOTLINES - 2, COLS - 2)) == (pair *) 0)
4230         return ((WINDOW *) 0);
4231     memcpy(&ul, tmp, sizeof(pair));
4232     MvAddCh(ul.y - 1, ul.x - 1, ACS_ULCORNER);
4233     move(0, 0);
4234     clrtoeol();
4235     addstr("Use arrows to move cursor, anything else to mark corner 2");
4236     refresh();
4237     if ((tmp = selectcell(ul.y, ul.x, LINES - BOTLINES - 2, COLS - 2)) ==
4238         (pair *) 0)
4239         return ((WINDOW *) 0);
4240     memcpy(&lr, tmp, sizeof(pair));
4241
4242     rwindow = subwin(stdscr, lr.y - ul.y + 1, lr.x - ul.x + 1, ul.y, ul.x);
4243
4244     outerbox(ul, lr, TRUE);
4245     refresh();
4246
4247     if (rwindow != 0)
4248         wrefresh(rwindow);
4249
4250     move(0, 0);
4251     clrtoeol();
4252     return (rwindow);
4253 }
4254
4255 static void
4256 newwin_move(FRAME * curp, int dy, int dx)
4257 {
4258     WINDOW *win = frame_win(curp);
4259     int cur_y, cur_x;
4260     int max_y, max_x;
4261
4262     getyx(win, cur_y, cur_x);
4263     getmaxyx(win, max_y, max_x);
4264     if ((cur_x += dx) < 0)
4265         cur_x = 0;
4266     else if (cur_x >= max_x)
4267         cur_x = max_x - 1;
4268     if ((cur_y += dy) < 0)
4269         cur_y = 0;
4270     else if (cur_y >= max_y)
4271         cur_y = max_y - 1;
4272     wmove(win, cur_y, cur_x);
4273 }
4274
4275 static FRAME *
4276 delete_framed(FRAME * fp, bool showit)
4277 {
4278     FRAME *np = 0;
4279
4280     if (fp != 0) {
4281         fp->last->next = fp->next;
4282         fp->next->last = fp->last;
4283
4284         if (showit) {
4285             werase(fp->wind);
4286             wrefresh(fp->wind);
4287         }
4288         delwin(fp->wind);
4289
4290         np = (fp == fp->next) ? 0 : fp->next;
4291         free(fp);
4292     }
4293     return np;
4294 }
4295
4296 static void
4297 acs_and_scroll(void)
4298 /* Demonstrate windows */
4299 {
4300     int c;
4301     FRAME *current = (FRAME *) 0, *neww;
4302     WINDOW *usescr;
4303 #if HAVE_PUTWIN && HAVE_GETWIN
4304     FILE *fp;
4305 #endif
4306
4307 #define DUMPFILE        "screendump"
4308
4309 #ifdef NCURSES_MOUSE_VERSION
4310     mousemask(BUTTON1_CLICKED, (mmask_t *) 0);
4311 #endif
4312     c = CTRL('C');
4313     raw();
4314     do {
4315         transient((FRAME *) 0, (char *) 0);
4316         switch (c) {
4317         case CTRL('C'):
4318             if ((neww = typeCalloc(FRAME, 1)) == 0) {
4319                 failed("acs_and_scroll");
4320                 goto breakout;
4321             }
4322             if ((neww->wind = getwindow()) == (WINDOW *) 0) {
4323                 failed("acs_and_scroll");
4324                 free(neww);
4325                 goto breakout;
4326             }
4327
4328             if (current == 0) { /* First element,  */
4329                 neww->next = neww;      /*   so point it at itself */
4330                 neww->last = neww;
4331             } else {
4332                 neww->next = current->next;
4333                 neww->last = current;
4334                 neww->last->next = neww;
4335                 neww->next->last = neww;
4336             }
4337             current = neww;
4338             /* SVr4 curses sets the keypad on all newly-created windows to
4339              * false.  Someone reported that PDCurses makes new windows inherit
4340              * this flag.  Remove the following 'keypad()' call to test this
4341              */
4342             keypad(current->wind, TRUE);
4343             current->do_keypad = HaveKeypad(current);
4344             current->do_scroll = HaveScroll(current);
4345             break;
4346
4347         case CTRL('N'): /* go to next window */
4348             if (current)
4349                 current = current->next;
4350             break;
4351
4352         case CTRL('P'): /* go to previous window */
4353             if (current)
4354                 current = current->last;
4355             break;
4356
4357         case CTRL('F'): /* scroll current window forward */
4358             if (current)
4359                 wscrl(frame_win(current), 1);
4360             break;
4361
4362         case CTRL('B'): /* scroll current window backwards */
4363             if (current)
4364                 wscrl(frame_win(current), -1);
4365             break;
4366
4367         case CTRL('K'): /* toggle keypad mode for current */
4368             if (current) {
4369                 current->do_keypad = !current->do_keypad;
4370                 keypad(current->wind, current->do_keypad);
4371             }
4372             break;
4373
4374         case CTRL('S'):
4375             if (current) {
4376                 current->do_scroll = !current->do_scroll;
4377                 scrollok(current->wind, current->do_scroll);
4378             }
4379             break;
4380
4381 #if HAVE_PUTWIN && HAVE_GETWIN
4382         case CTRL('W'): /* save and delete window */
4383             if ((current != 0) && (current == current->next)) {
4384                 transient(current, "Will not save/delete ONLY window");
4385                 break;
4386             } else if ((fp = fopen(DUMPFILE, "w")) == (FILE *) 0) {
4387                 transient(current, "Can't open screen dump file");
4388             } else {
4389                 (void) putwin(frame_win(current), fp);
4390                 (void) fclose(fp);
4391
4392                 current = delete_framed(current, TRUE);
4393             }
4394             break;
4395
4396         case CTRL('R'): /* restore window */
4397             if ((fp = fopen(DUMPFILE, "r")) == (FILE *) 0) {
4398                 transient(current, "Can't open screen dump file");
4399             } else {
4400                 if ((neww = typeCalloc(FRAME, 1)) != 0) {
4401
4402                     neww->next = current ? current->next : 0;
4403                     neww->last = current;
4404                     if (neww->last != 0)
4405                         neww->last->next = neww;
4406                     if (neww->next != 0)
4407                         neww->next->last = neww;
4408
4409                     neww->wind = getwin(fp);
4410
4411                     wrefresh(neww->wind);
4412                 } else {
4413                     failed("acs_and_scroll");
4414                 }
4415                 (void) fclose(fp);
4416             }
4417             break;
4418 #endif
4419
4420 #if HAVE_WRESIZE
4421         case CTRL('X'): /* resize window */
4422             if (current) {
4423                 pair *tmp, ul, lr;
4424                 int i, mx, my;
4425
4426                 move(0, 0);
4427                 clrtoeol();
4428                 addstr("Use arrows to move cursor, anything else to mark new corner");
4429                 refresh();
4430
4431                 getbegyx(current->wind, ul.y, ul.x);
4432
4433                 tmp = selectcell(ul.y, ul.x, LINES - BOTLINES - 2, COLS - 2);
4434                 if (tmp == (pair *) 0) {
4435                     beep();
4436                     break;
4437                 }
4438
4439                 getmaxyx(current->wind, lr.y, lr.x);
4440                 lr.y += (ul.y - 1);
4441                 lr.x += (ul.x - 1);
4442                 outerbox(ul, lr, FALSE);
4443                 wnoutrefresh(stdscr);
4444
4445                 /* strictly cosmetic hack for the test */
4446                 getmaxyx(current->wind, my, mx);
4447                 if (my > tmp->y - ul.y) {
4448                     getyx(current->wind, lr.y, lr.x);
4449                     wmove(current->wind, tmp->y - ul.y + 1, 0);
4450                     wclrtobot(current->wind);
4451                     wmove(current->wind, lr.y, lr.x);
4452                 }
4453                 if (mx > tmp->x - ul.x)
4454                     for (i = 0; i < my; i++) {
4455                         wmove(current->wind, i, tmp->x - ul.x + 1);
4456                         wclrtoeol(current->wind);
4457                     }
4458                 wnoutrefresh(current->wind);
4459
4460                 memcpy(&lr, tmp, sizeof(pair));
4461                 (void) wresize(current->wind, lr.y - ul.y + 0, lr.x - ul.x + 0);
4462
4463                 getbegyx(current->wind, ul.y, ul.x);
4464                 getmaxyx(current->wind, lr.y, lr.x);
4465                 lr.y += (ul.y - 1);
4466                 lr.x += (ul.x - 1);
4467                 outerbox(ul, lr, TRUE);
4468                 wnoutrefresh(stdscr);
4469
4470                 wnoutrefresh(current->wind);
4471                 move(0, 0);
4472                 clrtoeol();
4473                 doupdate();
4474             }
4475             break;
4476 #endif /* HAVE_WRESIZE */
4477
4478         case KEY_F(10): /* undocumented --- use this to test area clears */
4479             selectcell(0, 0, LINES - 1, COLS - 1);
4480             clrtobot();
4481             refresh();
4482             break;
4483
4484         case KEY_UP:
4485             newwin_move(current, -1, 0);
4486             break;
4487         case KEY_DOWN:
4488             newwin_move(current, 1, 0);
4489             break;
4490         case KEY_LEFT:
4491             newwin_move(current, 0, -1);
4492             break;
4493         case KEY_RIGHT:
4494             newwin_move(current, 0, 1);
4495             break;
4496
4497         case KEY_BACKSPACE:
4498             /* FALLTHROUGH */
4499         case KEY_DC:
4500             {
4501                 int y, x;
4502                 getyx(frame_win(current), y, x);
4503                 if (--x < 0) {
4504                     if (--y < 0)
4505                         break;
4506                     x = getmaxx(frame_win(current)) - 1;
4507                 }
4508                 (void) mvwdelch(frame_win(current), y, x);
4509             }
4510             break;
4511
4512         case '\r':
4513             c = '\n';
4514             /* FALLTHROUGH */
4515
4516         default:
4517             if (current)
4518                 waddch(current->wind, (chtype) c);
4519             else
4520                 beep();
4521             break;
4522         }
4523         newwin_report(current);
4524         usescr = frame_win(current);
4525         wrefresh(usescr);
4526     } while
4527         (!isQuit(c = wGetchar(usescr))
4528          && (c != ERR));
4529
4530   breakout:
4531     while (current != 0)
4532         current = delete_framed(current, FALSE);
4533
4534     scrollok(stdscr, TRUE);     /* reset to driver's default */
4535 #ifdef NCURSES_MOUSE_VERSION
4536     mousemask(0, (mmask_t *) 0);
4537 #endif
4538     noraw();
4539     erase();
4540     endwin();
4541 }
4542
4543 /****************************************************************************
4544  *
4545  * Panels tester
4546  *
4547  ****************************************************************************/
4548
4549 #if USE_LIBPANEL
4550 static int nap_msec = 1;
4551
4552 static NCURSES_CONST char *mod[] =
4553 {
4554     "test ",
4555     "TEST ",
4556     "(**) ",
4557     "*()* ",
4558     "<--> ",
4559     "LAST "
4560 };
4561
4562 /*+-------------------------------------------------------------------------
4563         wait_a_while(msec)
4564 --------------------------------------------------------------------------*/
4565 static void
4566 wait_a_while(int msec GCC_UNUSED)
4567 {
4568 #if HAVE_NAPMS
4569     if (nap_msec == 1)
4570         wGetchar(stdscr);
4571     else
4572         napms(nap_msec);
4573 #else
4574     if (nap_msec == 1)
4575         wGetchar(stdscr);
4576     else if (msec > 1000)
4577         sleep((unsigned) msec / 1000);
4578     else
4579         sleep(1);
4580 #endif
4581 }                               /* end of wait_a_while */
4582
4583 /*+-------------------------------------------------------------------------
4584         saywhat(text)
4585 --------------------------------------------------------------------------*/
4586 static void
4587 saywhat(NCURSES_CONST char *text)
4588 {
4589     wmove(stdscr, LINES - 1, 0);
4590     wclrtoeol(stdscr);
4591     if (text != 0 && *text != '\0') {
4592         waddstr(stdscr, text);
4593         waddstr(stdscr, "; ");
4594     }
4595     waddstr(stdscr, "press any key to continue");
4596 }                               /* end of saywhat */
4597
4598 /*+-------------------------------------------------------------------------
4599         mkpanel(rows,cols,tly,tlx) - alloc a win and panel and associate them
4600 --------------------------------------------------------------------------*/
4601 static PANEL *
4602 mkpanel(short color, int rows, int cols, int tly, int tlx)
4603 {
4604     WINDOW *win;
4605     PANEL *pan = 0;
4606
4607     if ((win = newwin(rows, cols, tly, tlx)) != 0) {
4608         if ((pan = new_panel(win)) == 0) {
4609             delwin(win);
4610         } else if (use_colors) {
4611             short fg = (short) ((color == COLOR_BLUE) ? COLOR_WHITE : COLOR_BLACK);
4612             short bg = color;
4613
4614             init_pair(color, fg, bg);
4615             wbkgdset(win, (attr_t) (COLOR_PAIR(color) | ' '));
4616         } else {
4617             wbkgdset(win, A_BOLD | ' ');
4618         }
4619     }
4620     return pan;
4621 }                               /* end of mkpanel */
4622
4623 /*+-------------------------------------------------------------------------
4624         rmpanel(pan)
4625 --------------------------------------------------------------------------*/
4626 static void
4627 rmpanel(PANEL * pan)
4628 {
4629     WINDOW *win = panel_window(pan);
4630     del_panel(pan);
4631     delwin(win);
4632 }                               /* end of rmpanel */
4633
4634 /*+-------------------------------------------------------------------------
4635         pflush()
4636 --------------------------------------------------------------------------*/
4637 static void
4638 pflush(void)
4639 {
4640     update_panels();
4641     doupdate();
4642 }                               /* end of pflush */
4643
4644 /*+-------------------------------------------------------------------------
4645         fill_panel(win)
4646 --------------------------------------------------------------------------*/
4647 static void
4648 init_panel(void)
4649 {
4650     register int y, x;
4651
4652     for (y = 0; y < LINES - 1; y++) {
4653         for (x = 0; x < COLS; x++)
4654             wprintw(stdscr, "%d", (y + x) % 10);
4655     }
4656 }
4657
4658 static void
4659 fill_panel(PANEL * pan)
4660 {
4661     WINDOW *win = panel_window(pan);
4662     const char *userptr = (const char *) panel_userptr(pan);
4663     int num = (userptr && *userptr) ? userptr[1] : '?';
4664     int y, x;
4665
4666     wmove(win, 1, 1);
4667     wprintw(win, "-pan%c-", num);
4668     wclrtoeol(win);
4669     box(win, 0, 0);
4670     for (y = 2; y < getmaxy(win) - 1; y++) {
4671         for (x = 1; x < getmaxx(win) - 1; x++) {
4672             wmove(win, y, x);
4673             waddch(win, UChar(num));
4674         }
4675     }
4676 }
4677
4678 #if USE_WIDEC_SUPPORT
4679 static void
4680 init_wide_panel(void)
4681 {
4682     int digit;
4683     cchar_t temp[10];
4684
4685     for (digit = 0; digit < 10; ++digit)
4686         make_fullwidth_digit(&temp[digit], digit);
4687
4688     do {
4689         int y, x;
4690         getyx(stdscr, y, x);
4691         digit = (y + x / 2) % 10;
4692     } while (add_wch(&temp[digit]) != ERR);
4693 }
4694
4695 static void
4696 fill_wide_panel(PANEL * pan)
4697 {
4698     WINDOW *win = panel_window(pan);
4699     const char *userptr = (const char *) panel_userptr(pan);
4700     int num = (userptr && *userptr) ? userptr[1] : '?';
4701     int y, x;
4702
4703     wmove(win, 1, 1);
4704     wprintw(win, "-pan%c-", num);
4705     wclrtoeol(win);
4706     box(win, 0, 0);
4707     for (y = 2; y < getmaxy(win) - 1; y++) {
4708         for (x = 1; x < getmaxx(win) - 1; x++) {
4709             wmove(win, y, x);
4710             waddch(win, UChar(num));
4711         }
4712     }
4713 }
4714 #endif
4715
4716 #define MAX_PANELS 5
4717
4718 static void
4719 canned_panel(PANEL * px[MAX_PANELS + 1], NCURSES_CONST char *cmd)
4720 {
4721     int which = cmd[1] - '0';
4722
4723     saywhat(cmd);
4724     switch (*cmd) {
4725     case 'h':
4726         hide_panel(px[which]);
4727         break;
4728     case 's':
4729         show_panel(px[which]);
4730         break;
4731     case 't':
4732         top_panel(px[which]);
4733         break;
4734     case 'b':
4735         bottom_panel(px[which]);
4736         break;
4737     case 'd':
4738         rmpanel(px[which]);
4739         break;
4740     }
4741     pflush();
4742     wait_a_while(nap_msec);
4743 }
4744
4745 static void
4746 demo_panels(void (*InitPanel) (void), void (*FillPanel) (PANEL *))
4747 {
4748     int count;
4749     int itmp;
4750     PANEL *px[MAX_PANELS + 1];
4751
4752     scrollok(stdscr, FALSE);    /* we don't want stdscr to scroll! */
4753     refresh();
4754
4755     InitPanel();
4756     for (count = 0; count < 5; count++) {
4757         px[1] = mkpanel(COLOR_RED,
4758                         LINES / 2 - 2,
4759                         COLS / 8 + 1,
4760                         0,
4761                         0);
4762         set_panel_userptr(px[1], (NCURSES_CONST void *) "p1");
4763
4764         px[2] = mkpanel(COLOR_GREEN,
4765                         LINES / 2 + 1,
4766                         COLS / 7,
4767                         LINES / 4,
4768                         COLS / 10);
4769         set_panel_userptr(px[2], (NCURSES_CONST void *) "p2");
4770
4771         px[3] = mkpanel(COLOR_YELLOW,
4772                         LINES / 4,
4773                         COLS / 10,
4774                         LINES / 2,
4775                         COLS / 9);
4776         set_panel_userptr(px[3], (NCURSES_CONST void *) "p3");
4777
4778         px[4] = mkpanel(COLOR_BLUE,
4779                         LINES / 2 - 2,
4780                         COLS / 8,
4781                         LINES / 2 - 2,
4782                         COLS / 3);
4783         set_panel_userptr(px[4], (NCURSES_CONST void *) "p4");
4784
4785         px[5] = mkpanel(COLOR_MAGENTA,
4786                         LINES / 2 - 2,
4787                         COLS / 8,
4788                         LINES / 2,
4789                         COLS / 2 - 2);
4790         set_panel_userptr(px[5], (NCURSES_CONST void *) "p5");
4791
4792         FillPanel(px[1]);
4793         FillPanel(px[2]);
4794         FillPanel(px[3]);
4795         FillPanel(px[4]);
4796         FillPanel(px[5]);
4797
4798         hide_panel(px[4]);
4799         hide_panel(px[5]);
4800         pflush();
4801         saywhat("");
4802         wait_a_while(nap_msec);
4803
4804         saywhat("h3 s1 s2 s4 s5");
4805         move_panel(px[1], 0, 0);
4806         hide_panel(px[3]);
4807         show_panel(px[1]);
4808         show_panel(px[2]);
4809         show_panel(px[4]);
4810         show_panel(px[5]);
4811         pflush();
4812         wait_a_while(nap_msec);
4813
4814         canned_panel(px, "s1");
4815         canned_panel(px, "s2");
4816
4817         saywhat("m2");
4818         move_panel(px[2], LINES / 3 + 1, COLS / 8);
4819         pflush();
4820         wait_a_while(nap_msec);
4821
4822         canned_panel(px, "s3");
4823
4824         saywhat("m3");
4825         move_panel(px[3], LINES / 4 + 1, COLS / 15);
4826         pflush();
4827         wait_a_while(nap_msec);
4828
4829         canned_panel(px, "b3");
4830         canned_panel(px, "s4");
4831         canned_panel(px, "s5");
4832         canned_panel(px, "t3");
4833         canned_panel(px, "t1");
4834         canned_panel(px, "t2");
4835         canned_panel(px, "t3");
4836         canned_panel(px, "t4");
4837
4838         for (itmp = 0; itmp < 6; itmp++) {
4839             WINDOW *w4 = panel_window(px[4]);
4840             WINDOW *w5 = panel_window(px[5]);
4841
4842             saywhat("m4");
4843             wmove(w4, LINES / 8, 1);
4844             waddstr(w4, mod[itmp]);
4845             move_panel(px[4], LINES / 6, itmp * (COLS / 8));
4846             wmove(w5, LINES / 6, 1);
4847             waddstr(w5, mod[itmp]);
4848             pflush();
4849             wait_a_while(nap_msec);
4850
4851             saywhat("m5");
4852             wmove(w4, LINES / 6, 1);
4853             waddstr(w4, mod[itmp]);
4854             move_panel(px[5], LINES / 3 - 1, (itmp * 10) + 6);
4855             wmove(w5, LINES / 8, 1);
4856             waddstr(w5, mod[itmp]);
4857             pflush();
4858             wait_a_while(nap_msec);
4859         }
4860
4861         saywhat("m4");
4862         move_panel(px[4], LINES / 6, itmp * (COLS / 8));
4863         pflush();
4864         wait_a_while(nap_msec);
4865
4866         canned_panel(px, "t5");
4867         canned_panel(px, "t2");
4868         canned_panel(px, "t1");
4869         canned_panel(px, "d2");
4870         canned_panel(px, "h3");
4871         canned_panel(px, "d1");
4872         canned_panel(px, "d4");
4873         canned_panel(px, "d5");
4874         canned_panel(px, "d3");
4875
4876         wait_a_while(nap_msec);
4877         if (nap_msec == 1)
4878             break;
4879         nap_msec = 100L;
4880     }
4881
4882     erase();
4883     endwin();
4884 }
4885 #endif /* USE_LIBPANEL */
4886
4887 /****************************************************************************
4888  *
4889  * Pad tester
4890  *
4891  ****************************************************************************/
4892
4893 #define GRIDSIZE        3
4894
4895 static bool pending_pan = FALSE;
4896 static bool show_panner_legend = TRUE;
4897
4898 static int
4899 panner_legend(int line)
4900 {
4901     static const char *const legend[] =
4902     {
4903         "Use arrow keys (or U,D,L,R) to pan, ESC to quit, ! to shell-out.",
4904         "Use +,- (or j,k) to grow/shrink the panner vertically.",
4905         "Use <,> (or h,l) to grow/shrink the panner horizontally.",
4906         "Number repeats.  Toggle legend:? filler:a timer:t scrollmark:s."
4907     };
4908     int n = ((int) SIZEOF(legend) - (LINES - line));
4909     if (n >= 0) {
4910         if (move(line, 0) != ERR) {
4911             if (show_panner_legend)
4912                 printw("%s", legend[n]);
4913             clrtoeol();
4914             return show_panner_legend;
4915         }
4916     }
4917     return FALSE;
4918 }
4919
4920 static void
4921 panner_h_cleanup(int from_y, int from_x, int to_x)
4922 {
4923     if (!panner_legend(from_y))
4924         do_h_line(from_y, from_x, ' ', to_x);
4925 }
4926
4927 static void
4928 panner_v_cleanup(int from_y, int from_x, int to_y)
4929 {
4930     if (!panner_legend(from_y))
4931         do_v_line(from_y, from_x, ' ', to_y);
4932 }
4933
4934 static void
4935 fill_pad(WINDOW *panpad, bool pan_lines)
4936 {
4937     int y, x;
4938     unsigned gridcount = 0;
4939
4940     wmove(panpad, 0, 0);
4941     for (y = 0; y < getmaxy(panpad); y++) {
4942         for (x = 0; x < getmaxx(panpad); x++) {
4943             if (y % GRIDSIZE == 0 && x % GRIDSIZE == 0) {
4944                 if (y == 0 && x == 0)
4945                     waddch(panpad, pan_lines ? ACS_ULCORNER : '+');
4946                 else if (y == 0)
4947                     waddch(panpad, pan_lines ? ACS_TTEE : '+');
4948                 else if (y == 0 || x == 0)
4949                     waddch(panpad, pan_lines ? ACS_LTEE : '+');
4950                 else
4951                     waddch(panpad, (chtype) ((pan_lines ? 'a' : 'A') +
4952                                              (int) (gridcount++ % 26)));
4953             } else if (y % GRIDSIZE == 0)
4954                 waddch(panpad, pan_lines ? ACS_HLINE : '-');
4955             else if (x % GRIDSIZE == 0)
4956                 waddch(panpad, pan_lines ? ACS_VLINE : '|');
4957             else
4958                 waddch(panpad, ' ');
4959         }
4960     }
4961 }
4962
4963 static void
4964 panner(WINDOW *pad,
4965        int top_x, int top_y, int porty, int portx,
4966        int (*pgetc) (WINDOW *))
4967 {
4968 #if HAVE_GETTIMEOFDAY
4969     struct timeval before, after;
4970     bool timing = TRUE;
4971 #endif
4972     bool pan_lines = FALSE;
4973     bool scrollers = TRUE;
4974     int basex = 0;
4975     int basey = 0;
4976     int pxmax, pymax, lowend, highend, c;
4977
4978     getmaxyx(pad, pymax, pxmax);
4979     scrollok(stdscr, FALSE);    /* we don't want stdscr to scroll! */
4980
4981     c = KEY_REFRESH;
4982     do {
4983 #ifdef NCURSES_VERSION
4984         /*
4985          * During shell-out, the user may have resized the window.  Adjust
4986          * the port size of the pad to accommodate this.  Ncurses automatically
4987          * resizes all of the normal windows to fit on the new screen.
4988          */
4989         if (top_x > COLS)
4990             top_x = COLS;
4991         if (portx > COLS)
4992             portx = COLS;
4993         if (top_y > LINES)
4994             top_y = LINES;
4995         if (porty > LINES)
4996             porty = LINES;
4997 #endif
4998         switch (c) {
4999         case KEY_REFRESH:
5000             erase();
5001
5002             /* FALLTHRU */
5003         case '?':
5004             if (c == '?')
5005                 show_panner_legend = !show_panner_legend;
5006             panner_legend(LINES - 4);
5007             panner_legend(LINES - 3);
5008             panner_legend(LINES - 2);
5009             panner_legend(LINES - 1);
5010             break;
5011         case 'a':
5012             pan_lines = !pan_lines;
5013             fill_pad(pad, pan_lines);
5014             pending_pan = FALSE;
5015             break;
5016
5017 #if HAVE_GETTIMEOFDAY
5018         case 't':
5019             timing = !timing;
5020             if (!timing)
5021                 panner_legend(LINES - 1);
5022             break;
5023 #endif
5024         case 's':
5025             scrollers = !scrollers;
5026             break;
5027
5028             /* Move the top-left corner of the pad, keeping the bottom-right
5029              * corner fixed.
5030              */
5031         case 'h':               /* increase-columns: move left edge to left */
5032             if (top_x <= 0)
5033                 beep();
5034             else {
5035                 panner_v_cleanup(top_y, top_x, porty);
5036                 top_x--;
5037             }
5038             break;
5039
5040         case 'j':               /* decrease-lines: move top-edge down */
5041             if (top_y >= porty)
5042                 beep();
5043             else {
5044                 panner_h_cleanup(top_y - 1, top_x - (top_x > 0), portx);
5045                 top_y++;
5046             }
5047             break;
5048
5049         case 'k':               /* increase-lines: move top-edge up */
5050             if (top_y <= 0)
5051                 beep();
5052             else {
5053                 top_y--;
5054                 panner_h_cleanup(top_y, top_x, portx);
5055             }
5056             break;
5057
5058         case 'l':               /* decrease-columns: move left-edge to right */
5059             if (top_x >= portx)
5060                 beep();
5061             else {
5062                 panner_v_cleanup(top_y - (top_y > 0), top_x - 1, porty);
5063                 top_x++;
5064             }
5065             break;
5066
5067             /* Move the bottom-right corner of the pad, keeping the top-left
5068              * corner fixed.
5069              */
5070         case KEY_IC:            /* increase-columns: move right-edge to right */
5071             if (portx >= pxmax || portx >= COLS)
5072                 beep();
5073             else {
5074                 panner_v_cleanup(top_y - (top_y > 0), portx - 1, porty);
5075                 ++portx;
5076             }
5077             break;
5078
5079         case KEY_IL:            /* increase-lines: move bottom-edge down */
5080             if (porty >= pymax || porty >= LINES)
5081                 beep();
5082             else {
5083                 panner_h_cleanup(porty - 1, top_x - (top_x > 0), portx);
5084                 ++porty;
5085             }
5086             break;
5087
5088         case KEY_DC:            /* decrease-columns: move bottom edge up */
5089             if (portx <= top_x)
5090                 beep();
5091             else {
5092                 portx--;
5093                 panner_v_cleanup(top_y - (top_y > 0), portx, porty);
5094             }
5095             break;
5096
5097         case KEY_DL:            /* decrease-lines */
5098             if (porty <= top_y)
5099                 beep();
5100             else {
5101                 porty--;
5102                 panner_h_cleanup(porty, top_x - (top_x > 0), portx);
5103             }
5104             break;
5105
5106         case KEY_LEFT:          /* pan leftwards */
5107             if (basex > 0)
5108                 basex--;
5109             else
5110                 beep();
5111             break;
5112
5113         case KEY_RIGHT: /* pan rightwards */
5114             if (basex + portx - (pymax > porty) < pxmax)
5115                 basex++;
5116             else
5117                 beep();
5118             break;
5119
5120         case KEY_UP:            /* pan upwards */
5121             if (basey > 0)
5122                 basey--;
5123             else
5124                 beep();
5125             break;
5126
5127         case KEY_DOWN:          /* pan downwards */
5128             if (basey + porty - (pxmax > portx) < pymax)
5129                 basey++;
5130             else
5131                 beep();
5132             break;
5133
5134         case 'H':
5135         case KEY_HOME:
5136         case KEY_FIND:
5137             basey = 0;
5138             break;
5139
5140         case 'E':
5141         case KEY_END:
5142         case KEY_SELECT:
5143             basey = pymax - porty;
5144             if (basey < 0)
5145                 basey = 0;
5146             break;
5147
5148         default:
5149             beep();
5150             break;
5151         }
5152
5153         MvAddCh(top_y - 1, top_x - 1, ACS_ULCORNER);
5154         do_v_line(top_y, top_x - 1, ACS_VLINE, porty);
5155         do_h_line(top_y - 1, top_x, ACS_HLINE, portx);
5156
5157         if (scrollers && (pxmax > portx - 1)) {
5158             int length = (portx - top_x - 1);
5159             float ratio = ((float) length) / ((float) pxmax);
5160
5161             lowend = (int) ((float) top_x + ((float) basex * ratio));
5162             highend = (int) ((float) top_x + ((float) (basex + length) * ratio));
5163
5164             do_h_line(porty - 1, top_x, ACS_HLINE, lowend);
5165             if (highend < portx) {
5166                 attron(A_REVERSE);
5167                 do_h_line(porty - 1, lowend, ' ', highend + 1);
5168                 attroff(A_REVERSE);
5169                 do_h_line(porty - 1, highend + 1, ACS_HLINE, portx);
5170             }
5171         } else
5172             do_h_line(porty - 1, top_x, ACS_HLINE, portx);
5173
5174         if (scrollers && (pymax > porty - 1)) {
5175             int length = (porty - top_y - 1);
5176             float ratio = ((float) length) / ((float) pymax);
5177
5178             lowend = (int) ((float) top_y + ((float) basey * ratio));
5179             highend = (int) ((float) top_y + ((float) (basey + length) * ratio));
5180
5181             do_v_line(top_y, portx - 1, ACS_VLINE, lowend);
5182             if (highend < porty) {
5183                 attron(A_REVERSE);
5184                 do_v_line(lowend, portx - 1, ' ', highend + 1);
5185                 attroff(A_REVERSE);
5186                 do_v_line(highend + 1, portx - 1, ACS_VLINE, porty);
5187             }
5188         } else
5189             do_v_line(top_y, portx - 1, ACS_VLINE, porty);
5190
5191         MvAddCh(top_y - 1, portx - 1, ACS_URCORNER);
5192         MvAddCh(porty - 1, top_x - 1, ACS_LLCORNER);
5193         MvAddCh(porty - 1, portx - 1, ACS_LRCORNER);
5194
5195         if (!pending_pan) {
5196 #if HAVE_GETTIMEOFDAY
5197             gettimeofday(&before, 0);
5198 #endif
5199             wnoutrefresh(stdscr);
5200
5201             pnoutrefresh(pad,
5202                          basey, basex,
5203                          top_y, top_x,
5204                          porty - (pxmax > portx) - 1,
5205                          portx - (pymax > porty) - 1);
5206
5207             doupdate();
5208 #if HAVE_GETTIMEOFDAY
5209 #define TIMEVAL2S(data) ((double) data.tv_sec + ((double) data.tv_usec / 1.0e6))
5210             if (timing) {
5211                 double elapsed;
5212                 gettimeofday(&after, 0);
5213                 elapsed = (TIMEVAL2S(after) - TIMEVAL2S(before));
5214                 move(LINES - 1, COLS - 12);
5215                 printw("Secs: %2.03f", elapsed);
5216                 refresh();
5217             }
5218 #endif
5219         }
5220
5221     } while
5222         ((c = pgetc(pad)) != KEY_EXIT);
5223
5224     scrollok(stdscr, TRUE);     /* reset to driver's default */
5225 }
5226
5227 static int
5228 padgetch(WINDOW *win)
5229 {
5230     static int count;
5231     static int last;
5232     int c;
5233
5234     if ((pending_pan = (count > 0)) != FALSE) {
5235         count--;
5236         pending_pan = (count != 0);
5237     } else {
5238         for (;;) {
5239             switch (c = wGetchar(win)) {
5240             case '!':
5241                 ShellOut(FALSE);
5242                 /* FALLTHRU */
5243             case CTRL('r'):
5244                 endwin();
5245                 refresh();
5246                 c = KEY_REFRESH;
5247                 break;
5248             case CTRL('l'):
5249                 c = KEY_REFRESH;
5250                 break;
5251             case 'U':
5252                 c = KEY_UP;
5253                 break;
5254             case 'D':
5255                 c = KEY_DOWN;
5256                 break;
5257             case 'R':
5258                 c = KEY_RIGHT;
5259                 break;
5260             case 'L':
5261                 c = KEY_LEFT;
5262                 break;
5263             case '+':
5264                 c = KEY_IL;
5265                 break;
5266             case '-':
5267                 c = KEY_DL;
5268                 break;
5269             case '>':
5270                 c = KEY_IC;
5271                 break;
5272             case '<':
5273                 c = KEY_DC;
5274                 break;
5275             case ERR:           /* FALLTHRU */
5276             case case_QUIT:
5277                 count = 0;
5278                 c = KEY_EXIT;
5279                 break;
5280             default:
5281                 if (c >= '0' && c <= '9') {
5282                     count = count * 10 + (c - '0');
5283                     continue;
5284                 }
5285                 break;
5286             }
5287             last = c;
5288             break;
5289         }
5290         if (count > 0)
5291             count--;
5292     }
5293     return (last);
5294 }
5295
5296 #define PAD_HIGH 200
5297 #define PAD_WIDE 200
5298
5299 static void
5300 demo_pad(void)
5301 /* Demonstrate pads. */
5302 {
5303     WINDOW *panpad = newpad(PAD_HIGH, PAD_WIDE);
5304
5305     if (panpad == 0) {
5306         Cannot("cannot create requested pad");
5307         return;
5308     }
5309
5310     fill_pad(panpad, FALSE);
5311
5312     panner_legend(LINES - 4);
5313     panner_legend(LINES - 3);
5314     panner_legend(LINES - 2);
5315     panner_legend(LINES - 1);
5316
5317     keypad(panpad, TRUE);
5318
5319     /* Make the pad (initially) narrow enough that a trace file won't wrap.
5320      * We'll still be able to widen it during a test, since that's required
5321      * for testing boundaries.
5322      */
5323     panner(panpad, 2, 2, LINES - 5, COLS - 15, padgetch);
5324
5325     delwin(panpad);
5326     endwin();
5327     erase();
5328 }
5329
5330 /****************************************************************************
5331  *
5332  * Tests from John Burnell's PDCurses tester
5333  *
5334  ****************************************************************************/
5335
5336 static void
5337 Continue(WINDOW *win)
5338 {
5339     noecho();
5340     wmove(win, 10, 1);
5341     MvWAddStr(win, 10, 1, " Press any key to continue");
5342     wrefresh(win);
5343     wGetchar(win);
5344 }
5345
5346 static void
5347 flushinp_test(WINDOW *win)
5348 /* Input test, adapted from John Burnell's PDCurses tester */
5349 {
5350     int w, h, bx, by, sw, sh, i;
5351
5352     WINDOW *subWin;
5353     wclear(win);
5354
5355     getmaxyx(win, h, w);
5356     getbegyx(win, by, bx);
5357     sw = w / 3;
5358     sh = h / 3;
5359     if ((subWin = subwin(win, sh, sw, by + h - sh - 2, bx + w - sw - 2)) == 0)
5360         return;
5361
5362 #ifdef A_COLOR
5363     if (use_colors) {
5364         init_pair(2, COLOR_CYAN, COLOR_BLUE);
5365         wbkgd(subWin, (chtype) (COLOR_PAIR(2) | ' '));
5366     }
5367 #endif
5368     (void) wattrset(subWin, A_BOLD);
5369     box(subWin, ACS_VLINE, ACS_HLINE);
5370     MvWAddStr(subWin, 2, 1, "This is a subwindow");
5371     wrefresh(win);
5372
5373     /*
5374      * This used to set 'nocbreak()'.  However, Alexander Lukyanov says that
5375      * it only happened to "work" on SVr4 because that implementation does not
5376      * emulate nocbreak+noecho mode, whereas ncurses does.  To get the desired
5377      * test behavior, we're using 'cbreak()', which will allow a single
5378      * character to return without needing a newline. - T.Dickey 1997/10/11.
5379      */
5380     cbreak();
5381     MvWAddStr(win, 0, 1, "This is a test of the flushinp() call.");
5382
5383     MvWAddStr(win, 2, 1, "Type random keys for 5 seconds.");
5384     MvWAddStr(win, 3, 1,
5385               "These should be discarded (not echoed) after the subwindow goes away.");
5386     wrefresh(win);
5387
5388     for (i = 0; i < 5; i++) {
5389         MvWPrintw(subWin, 1, 1, "Time = %d", i);
5390         wrefresh(subWin);
5391         napms(1000);
5392         flushinp();
5393     }
5394
5395     delwin(subWin);
5396     werase(win);
5397     flash();
5398     wrefresh(win);
5399     napms(1000);
5400
5401     MvWAddStr(win, 2, 1,
5402               "If you were still typing when the window timer expired,");
5403     MvWAddStr(win, 3, 1,
5404               "or else you typed nothing at all while it was running,");
5405     MvWAddStr(win, 4, 1,
5406               "test was invalid.  You'll see garbage or nothing at all. ");
5407     MvWAddStr(win, 6, 1, "Press a key");
5408     wmove(win, 9, 10);
5409     wrefresh(win);
5410     echo();
5411     wGetchar(win);
5412     flushinp();
5413     MvWAddStr(win, 12, 0,
5414               "If you see any key other than what you typed, flushinp() is broken.");
5415     Continue(win);
5416
5417     wmove(win, 9, 10);
5418     wdelch(win);
5419     wrefresh(win);
5420     wmove(win, 12, 0);
5421     clrtoeol();
5422     waddstr(win,
5423             "What you typed should now have been deleted; if not, wdelch() failed.");
5424     Continue(win);
5425
5426     cbreak();
5427 }
5428
5429 /****************************************************************************
5430  *
5431  * Menu test
5432  *
5433  ****************************************************************************/
5434
5435 #if USE_LIBMENU
5436
5437 #define MENU_Y  8
5438 #define MENU_X  8
5439
5440 static int
5441 menu_virtualize(int c)
5442 {
5443     if (c == '\n' || c == KEY_EXIT)
5444         return (MAX_COMMAND + 1);
5445     else if (c == 'u')
5446         return (REQ_SCR_ULINE);
5447     else if (c == 'd')
5448         return (REQ_SCR_DLINE);
5449     else if (c == 'b' || c == KEY_NPAGE)
5450         return (REQ_SCR_UPAGE);
5451     else if (c == 'f' || c == KEY_PPAGE)
5452         return (REQ_SCR_DPAGE);
5453     else if (c == 'n' || c == KEY_DOWN)
5454         return (REQ_NEXT_ITEM);
5455     else if (c == 'p' || c == KEY_UP)
5456         return (REQ_PREV_ITEM);
5457     else if (c == ' ')
5458         return (REQ_TOGGLE_ITEM);
5459     else {
5460         if (c != KEY_MOUSE)
5461             beep();
5462         return (c);
5463     }
5464 }
5465
5466 static CONST_MENUS char *animals[] =
5467 {
5468     "Lions",
5469     "Tigers",
5470     "Bears",
5471     "(Oh my!)",
5472     "Newts",
5473     "Platypi",
5474     "Lemurs",
5475     "(Oh really?!)",
5476     "Leopards",
5477     "Panthers",
5478     "Pumas",
5479     "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
5480     "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs, Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
5481     (char *) 0
5482 };
5483
5484 static void
5485 menu_test(void)
5486 {
5487     MENU *m;
5488     ITEM *items[SIZEOF(animals)];
5489     ITEM **ip = items;
5490     CONST_MENUS char **ap;
5491     int mrows, mcols, c;
5492     WINDOW *menuwin;
5493
5494 #ifdef NCURSES_MOUSE_VERSION
5495     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
5496 #endif
5497     MvAddStr(0, 0, "This is the menu test:");
5498     MvAddStr(2, 0, "  Use up and down arrow to move the select bar.");
5499     MvAddStr(3, 0, "  'n' and 'p' act like arrows.");
5500     MvAddStr(4, 0,
5501              "  'b' and 'f' scroll up/down (page), 'u' and 'd' (line).");
5502     MvAddStr(5, 0, "  Press return to exit.");
5503     refresh();
5504
5505     for (ap = animals; *ap; ap++) {
5506         if ((*ip = new_item(*ap, "")) != 0)
5507             ++ip;
5508     }
5509     *ip = (ITEM *) 0;
5510
5511     m = new_menu(items);
5512
5513     set_menu_format(m, (SIZEOF(animals) + 1) / 2, 1);
5514     scale_menu(m, &mrows, &mcols);
5515
5516     menuwin = newwin(mrows + 2, mcols + 2, MENU_Y, MENU_X);
5517     set_menu_win(m, menuwin);
5518     keypad(menuwin, TRUE);
5519     box(menuwin, 0, 0);
5520
5521     set_menu_sub(m, derwin(menuwin, mrows, mcols, 1, 1));
5522
5523     post_menu(m);
5524
5525     while ((c = menu_driver(m, menu_virtualize(wGetchar(menuwin)))) != E_UNKNOWN_COMMAND) {
5526         if (c == E_NOT_POSTED)
5527             break;
5528         if (c == E_REQUEST_DENIED)
5529             beep();
5530         continue;
5531     }
5532
5533     MvPrintw(LINES - 2, 0,
5534              "You chose: %s\n", item_name(current_item(m)));
5535     (void) addstr("Press any key to continue...");
5536     wGetchar(stdscr);
5537
5538     unpost_menu(m);
5539     delwin(menuwin);
5540
5541     free_menu(m);
5542     for (ip = items; *ip; ip++)
5543         free_item(*ip);
5544 #ifdef NCURSES_MOUSE_VERSION
5545     mousemask(0, (mmask_t *) 0);
5546 #endif
5547 }
5548
5549 #ifdef TRACE
5550 #define T_TBL(name) { #name, name }
5551 static struct {
5552     const char *name;
5553     unsigned mask;
5554 } t_tbl[] = {
5555
5556     T_TBL(TRACE_DISABLE),
5557         T_TBL(TRACE_TIMES),
5558         T_TBL(TRACE_TPUTS),
5559         T_TBL(TRACE_UPDATE),
5560         T_TBL(TRACE_MOVE),
5561         T_TBL(TRACE_CHARPUT),
5562         T_TBL(TRACE_ORDINARY),
5563         T_TBL(TRACE_CALLS),
5564         T_TBL(TRACE_VIRTPUT),
5565         T_TBL(TRACE_IEVENT),
5566         T_TBL(TRACE_BITS),
5567         T_TBL(TRACE_ICALLS),
5568         T_TBL(TRACE_CCALLS),
5569         T_TBL(TRACE_DATABASE),
5570         T_TBL(TRACE_ATTRS),
5571         T_TBL(TRACE_MAXIMUM),
5572     {
5573         (char *) 0, 0
5574     }
5575 };
5576
5577 static char *
5578 tracetrace(unsigned tlevel)
5579 {
5580     static char *buf;
5581     int n;
5582
5583     if (buf == 0) {
5584         size_t need = 12;
5585         for (n = 0; t_tbl[n].name != 0; n++)
5586             need += strlen(t_tbl[n].name) + 2;
5587         buf = typeMalloc(char, need);
5588         if (!buf)
5589             failed("tracetrace");
5590     }
5591     sprintf(buf, "0x%02x = {", tlevel);
5592     if (tlevel == 0) {
5593         sprintf(buf + strlen(buf), "%s, ", t_tbl[0].name);
5594     } else {
5595         for (n = 1; t_tbl[n].name != 0; n++)
5596             if ((tlevel & t_tbl[n].mask) == t_tbl[n].mask) {
5597                 strcat(buf, t_tbl[n].name);
5598                 strcat(buf, ", ");
5599             }
5600     }
5601     if (buf[strlen(buf) - 2] == ',')
5602         buf[strlen(buf) - 2] = '\0';
5603     return (strcat(buf, "}"));
5604 }
5605
5606 /* fake a dynamically reconfigurable menu using the 0th entry to deselect
5607  * the others
5608  */
5609 static int
5610 run_trace_menu(MENU * m)
5611 {
5612     ITEM **items;
5613     ITEM *i, **p;
5614
5615     for (;;) {
5616         bool changed = FALSE;
5617         switch (menu_driver(m, menu_virtualize(wGetchar(menu_win(m))))) {
5618         case E_UNKNOWN_COMMAND:
5619             return FALSE;
5620         default:
5621             items = menu_items(m);
5622             i = current_item(m);
5623             if (i == items[0]) {
5624                 if (item_value(i)) {
5625                     for (p = items + 1; *p != 0; p++)
5626                         if (item_value(*p)) {
5627                             set_item_value(*p, FALSE);
5628                             changed = TRUE;
5629                         }
5630                 }
5631             } else {
5632                 for (p = items + 1; *p != 0; p++)
5633                     if (item_value(*p)) {
5634                         set_item_value(items[0], FALSE);
5635                         changed = TRUE;
5636                         break;
5637                     }
5638             }
5639             if (!changed)
5640                 return TRUE;
5641         }
5642     }
5643 }
5644
5645 static void
5646 trace_set(void)
5647 /* interactively set the trace level */
5648 {
5649     MENU *m;
5650     ITEM *items[SIZEOF(t_tbl)];
5651     ITEM **ip = items;
5652     int mrows, mcols;
5653     unsigned newtrace;
5654     int n;
5655     WINDOW *menuwin;
5656
5657     MvAddStr(0, 0, "Interactively set trace level:");
5658     MvAddStr(2, 0, "  Press space bar to toggle a selection.");
5659     MvAddStr(3, 0, "  Use up and down arrow to move the select bar.");
5660     MvAddStr(4, 0, "  Press return to set the trace level.");
5661     MvPrintw(6, 0, "(Current trace level is %s)", tracetrace(_nc_tracing));
5662
5663     refresh();
5664
5665     for (n = 0; t_tbl[n].name != 0; n++) {
5666         if ((*ip = new_item(t_tbl[n].name, "")) != 0) {
5667             ++ip;
5668         }
5669     }
5670     *ip = (ITEM *) 0;
5671
5672     m = new_menu(items);
5673
5674     set_menu_format(m, 0, 2);
5675     scale_menu(m, &mrows, &mcols);
5676
5677     menu_opts_off(m, O_ONEVALUE);
5678     menuwin = newwin(mrows + 2, mcols + 2, MENU_Y, MENU_X);
5679     set_menu_win(m, menuwin);
5680     keypad(menuwin, TRUE);
5681     box(menuwin, 0, 0);
5682
5683     set_menu_sub(m, derwin(menuwin, mrows, mcols, 1, 1));
5684
5685     post_menu(m);
5686
5687     for (ip = menu_items(m); *ip; ip++) {
5688         unsigned mask = t_tbl[item_index(*ip)].mask;
5689         if (mask == 0)
5690             set_item_value(*ip, _nc_tracing == 0);
5691         else if ((mask & _nc_tracing) == mask)
5692             set_item_value(*ip, TRUE);
5693     }
5694
5695     while (run_trace_menu(m))
5696         continue;
5697
5698     newtrace = 0;
5699     for (ip = menu_items(m); *ip; ip++)
5700         if (item_value(*ip))
5701             newtrace |= t_tbl[item_index(*ip)].mask;
5702     trace(newtrace);
5703     Trace(("trace level interactively set to %s", tracetrace(_nc_tracing)));
5704
5705     MvPrintw(LINES - 2, 0,
5706              "Trace level is %s\n", tracetrace(_nc_tracing));
5707     (void) addstr("Press any key to continue...");
5708     wGetchar(stdscr);
5709
5710     unpost_menu(m);
5711     delwin(menuwin);
5712
5713     free_menu(m);
5714     for (ip = items; *ip; ip++)
5715         free_item(*ip);
5716 }
5717 #endif /* TRACE */
5718 #endif /* USE_LIBMENU */
5719
5720 /****************************************************************************
5721  *
5722  * Forms test
5723  *
5724  ****************************************************************************/
5725 #if USE_LIBFORM
5726 static FIELD *
5727 make_label(int frow, int fcol, NCURSES_CONST char *label)
5728 {
5729     FIELD *f = new_field(1, (int) strlen(label), frow, fcol, 0, 0);
5730
5731     if (f) {
5732         set_field_buffer(f, 0, label);
5733         set_field_opts(f, (int) ((unsigned) field_opts(f) & ~O_ACTIVE));
5734     }
5735     return (f);
5736 }
5737
5738 static FIELD *
5739 make_field(int frow, int fcol, int rows, int cols, bool secure)
5740 {
5741     FIELD *f = new_field(rows, cols, frow, fcol, 0, secure ? 1 : 0);
5742
5743     if (f) {
5744         set_field_back(f, A_UNDERLINE);
5745         set_field_userptr(f, (void *) 0);
5746     }
5747     return (f);
5748 }
5749
5750 static void
5751 display_form(FORM * f)
5752 {
5753     WINDOW *w;
5754     int rows, cols;
5755
5756     scale_form(f, &rows, &cols);
5757
5758     if ((w = newwin(rows + 2, cols + 4, 0, 0)) != (WINDOW *) 0) {
5759         set_form_win(f, w);
5760         set_form_sub(f, derwin(w, rows, cols, 1, 2));
5761         box(w, 0, 0);
5762         keypad(w, TRUE);
5763         if (post_form(f) != E_OK)
5764             wrefresh(w);
5765     }
5766 }
5767
5768 static void
5769 erase_form(FORM * f)
5770 {
5771     WINDOW *w = form_win(f);
5772     WINDOW *s = form_sub(f);
5773
5774     unpost_form(f);
5775     werase(w);
5776     wrefresh(w);
5777     delwin(s);
5778     delwin(w);
5779 }
5780
5781 static int
5782 edit_secure(FIELD * me, int c)
5783 {
5784     int rows, cols, frow, fcol, nrow, nbuf;
5785
5786     if (field_info(me, &rows, &cols, &frow, &fcol, &nrow, &nbuf) == E_OK
5787         && nbuf > 0) {
5788         char *source = field_buffer(me, 1);
5789         size_t have = (source ? strlen(source) : 0) + 1;
5790         size_t need = 80 + have;
5791         char *temp = malloc(need);
5792         long len;
5793
5794         if (temp != 0) {
5795             strncpy(temp, source ? source : "", have + 1);
5796             len = (long) (char *) field_userptr(me);
5797             if (c <= KEY_MAX) {
5798                 if (isgraph(c) && (len + 1) < (int) sizeof(temp)) {
5799                     temp[len++] = (char) c;
5800                     temp[len] = 0;
5801                     set_field_buffer(me, 1, temp);
5802                     c = '*';
5803                 } else {
5804                     c = 0;
5805                 }
5806             } else {
5807                 switch (c) {
5808                 case REQ_BEG_FIELD:
5809                 case REQ_CLR_EOF:
5810                 case REQ_CLR_EOL:
5811                 case REQ_DEL_LINE:
5812                 case REQ_DEL_WORD:
5813                 case REQ_DOWN_CHAR:
5814                 case REQ_END_FIELD:
5815                 case REQ_INS_CHAR:
5816                 case REQ_INS_LINE:
5817                 case REQ_LEFT_CHAR:
5818                 case REQ_NEW_LINE:
5819                 case REQ_NEXT_WORD:
5820                 case REQ_PREV_WORD:
5821                 case REQ_RIGHT_CHAR:
5822                 case REQ_UP_CHAR:
5823                     c = 0;      /* we don't want to do inline editing */
5824                     break;
5825                 case REQ_CLR_FIELD:
5826                     if (len) {
5827                         temp[0] = 0;
5828                         set_field_buffer(me, 1, temp);
5829                     }
5830                     break;
5831                 case REQ_DEL_CHAR:
5832                 case REQ_DEL_PREV:
5833                     if (len) {
5834                         temp[--len] = 0;
5835                         set_field_buffer(me, 1, temp);
5836                     }
5837                     break;
5838                 }
5839             }
5840             set_field_userptr(me, (void *) len);
5841             free(temp);
5842         }
5843     }
5844     return c;
5845 }
5846
5847 static int
5848 form_virtualize(FORM * f, WINDOW *w)
5849 {
5850     /* *INDENT-OFF* */
5851     static const struct {
5852         int code;
5853         int result;
5854     } lookup[] = {
5855         { CTRL('A'),    REQ_NEXT_CHOICE },
5856         { CTRL('B'),    REQ_PREV_WORD },
5857         { CTRL('C'),    REQ_CLR_EOL },
5858         { CTRL('D'),    REQ_DOWN_FIELD },
5859         { CTRL('E'),    REQ_END_FIELD },
5860         { CTRL('F'),    REQ_NEXT_PAGE },
5861         { CTRL('G'),    REQ_DEL_WORD },
5862         { CTRL('H'),    REQ_DEL_PREV },
5863         { CTRL('I'),    REQ_INS_CHAR },
5864         { CTRL('K'),    REQ_CLR_EOF },
5865         { CTRL('L'),    REQ_LEFT_FIELD },
5866         { CTRL('M'),    REQ_NEW_LINE },
5867         { CTRL('N'),    REQ_NEXT_FIELD },
5868         { CTRL('O'),    REQ_INS_LINE },
5869         { CTRL('P'),    REQ_PREV_FIELD },
5870         { CTRL('R'),    REQ_RIGHT_FIELD },
5871         { CTRL('S'),    REQ_BEG_FIELD },
5872         { CTRL('U'),    REQ_UP_FIELD },
5873         { CTRL('V'),    REQ_DEL_CHAR },
5874         { CTRL('W'),    REQ_NEXT_WORD },
5875         { CTRL('X'),    REQ_CLR_FIELD },
5876         { CTRL('Y'),    REQ_DEL_LINE },
5877         { CTRL('Z'),    REQ_PREV_CHOICE },
5878         { ESCAPE,       MAX_FORM_COMMAND + 1 },
5879         { KEY_BACKSPACE, REQ_DEL_PREV },
5880         { KEY_DOWN,     REQ_DOWN_CHAR },
5881         { KEY_END,      REQ_LAST_FIELD },
5882         { KEY_HOME,     REQ_FIRST_FIELD },
5883         { KEY_LEFT,     REQ_LEFT_CHAR },
5884         { KEY_LL,       REQ_LAST_FIELD },
5885         { KEY_NEXT,     REQ_NEXT_FIELD },
5886         { KEY_NPAGE,    REQ_NEXT_PAGE },
5887         { KEY_PPAGE,    REQ_PREV_PAGE },
5888         { KEY_PREVIOUS, REQ_PREV_FIELD },
5889         { KEY_RIGHT,    REQ_RIGHT_CHAR },
5890         { KEY_UP,       REQ_UP_CHAR },
5891         { QUIT,         MAX_FORM_COMMAND + 1 }
5892     };
5893     /* *INDENT-ON* */
5894
5895     static int mode = REQ_INS_MODE;
5896     int c = wGetchar(w);
5897     unsigned n;
5898     FIELD *me = current_field(f);
5899     bool current = TRUE;
5900
5901     if (c == CTRL(']')) {
5902         if (mode == REQ_INS_MODE) {
5903             mode = REQ_OVL_MODE;
5904         } else {
5905             mode = REQ_INS_MODE;
5906         }
5907         c = mode;
5908     } else {
5909         for (n = 0; n < SIZEOF(lookup); n++) {
5910             if (lookup[n].code == c) {
5911                 c = lookup[n].result;
5912                 break;
5913             }
5914         }
5915     }
5916     MvPrintw(0, COLS - 6, "(%s)", mode == REQ_INS_MODE ? "INS" : "OVL");
5917
5918     /*
5919      * Force the field that the user is typing into to be in reverse video,
5920      * while the other fields are shown underlined.
5921      */
5922     switch (c) {
5923     case REQ_BEG_FIELD:
5924     case REQ_CLR_EOF:
5925     case REQ_CLR_EOL:
5926     case REQ_CLR_FIELD:
5927     case REQ_DEL_CHAR:
5928     case REQ_DEL_LINE:
5929     case REQ_DEL_PREV:
5930     case REQ_DEL_WORD:
5931     case REQ_END_FIELD:
5932     case REQ_INS_CHAR:
5933     case REQ_INS_LINE:
5934     case REQ_LEFT_CHAR:
5935     case REQ_LEFT_FIELD:
5936     case REQ_NEXT_WORD:
5937     case REQ_RIGHT_CHAR:
5938         current = TRUE;
5939         break;
5940     default:
5941         current = (c < KEY_MAX);
5942         break;
5943     }
5944     if (current) {
5945         c = edit_secure(me, c);
5946         set_field_back(me, A_REVERSE);
5947     } else {
5948         c = edit_secure(me, c);
5949         set_field_back(me, A_UNDERLINE);
5950     }
5951     return c;
5952 }
5953
5954 static int
5955 my_form_driver(FORM * form, int c)
5956 {
5957     if (c == (MAX_FORM_COMMAND + 1)
5958         && form_driver(form, REQ_VALIDATION) == E_OK)
5959         return (TRUE);
5960     else {
5961         beep();
5962         return (FALSE);
5963     }
5964 }
5965
5966 #ifdef NCURSES_VERSION
5967 #define FIELDCHECK_CB(func) bool func(FIELD * fld, const void * data GCC_UNUSED)
5968 #define CHAR_CHECK_CB(func) bool func(int ch, const void *data GCC_UNUSED)
5969 #else
5970 #define FIELDCHECK_CB(func) int func(FIELD * fld, char * data GCC_UNUSED)
5971 #define CHAR_CHECK_CB(func) int func(int ch, char *data GCC_UNUSED)
5972 #endif
5973
5974 /*
5975  * Allow a middle initial, optionally with a '.' to end it.
5976  */
5977 static
5978 FIELDCHECK_CB(mi_field_check)
5979 {
5980     char *s = field_buffer(fld, 0);
5981     int state = 0;
5982     int n;
5983
5984     for (n = 0; s[n] != '\0'; ++n) {
5985         switch (state) {
5986         case 0:
5987             if (s[n] == '.') {
5988                 if (n != 1)
5989                     return FALSE;
5990                 state = 2;
5991             } else if (isspace(UChar(s[n]))) {
5992                 state = 2;
5993             }
5994             break;
5995         case 2:
5996             if (!isspace(UChar(s[n])))
5997                 return FALSE;
5998             break;
5999         }
6000     }
6001
6002     /* force the form to display a leading capital */
6003     if (islower(UChar(s[0]))) {
6004         s[0] = (char) toupper(UChar(s[0]));
6005         set_field_buffer(fld, 0, s);
6006     }
6007     return TRUE;
6008 }
6009
6010 static
6011 CHAR_CHECK_CB(mi_char_check)
6012 {
6013     return ((isalpha(ch) || ch == '.') ? TRUE : FALSE);
6014 }
6015
6016 /*
6017  * Passwords should be at least 6 characters.
6018  */
6019 static
6020 FIELDCHECK_CB(pw_field_check)
6021 {
6022     char *s = field_buffer(fld, 0);
6023     int n;
6024
6025     for (n = 0; s[n] != '\0'; ++n) {
6026         if (isspace(UChar(s[n]))) {
6027             if (n < 6)
6028                 return FALSE;
6029         }
6030     }
6031     return TRUE;
6032 }
6033
6034 static
6035 CHAR_CHECK_CB(pw_char_check)
6036 {
6037     return (isgraph(ch) ? TRUE : FALSE);
6038 }
6039
6040 static void
6041 demo_forms(void)
6042 {
6043     WINDOW *w;
6044     FORM *form;
6045     FIELD *f[12], *secure;
6046     FIELDTYPE *fty_middle = new_fieldtype(mi_field_check, mi_char_check);
6047     FIELDTYPE *fty_passwd = new_fieldtype(pw_field_check, pw_char_check);
6048     int finished = 0, c;
6049     unsigned n = 0;
6050
6051 #ifdef NCURSES_MOUSE_VERSION
6052     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
6053 #endif
6054
6055     move(18, 0);
6056     addstr("Defined edit/traversal keys:   ^Q/ESC- exit form\n");
6057     addstr("^N   -- go to next field       ^P  -- go to previous field\n");
6058     addstr("Home -- go to first field      End -- go to last field\n");
6059     addstr("^L   -- go to field to left    ^R  -- go to field to right\n");
6060     addstr("^U   -- move upward to field   ^D  -- move downward to field\n");
6061     addstr("^W   -- go to next word        ^B  -- go to previous word\n");
6062     addstr("^S   -- go to start of field   ^E  -- go to end of field\n");
6063     addstr("^H   -- delete previous char   ^Y  -- delete line\n");
6064     addstr("^G   -- delete current word    ^C  -- clear to end of line\n");
6065     addstr("^K   -- clear to end of field  ^X  -- clear field\n");
6066     addstr("Arrow keys move within a field as you would expect. ^] toggles overlay mode.");
6067
6068     MvAddStr(4, 57, "Forms Entry Test");
6069
6070     refresh();
6071
6072     /* describe the form */
6073     memset(f, 0, sizeof(f));
6074     f[n++] = make_label(0, 15, "Sample Form");
6075
6076     f[n++] = make_label(2, 0, "Last Name");
6077     f[n++] = make_field(3, 0, 1, 18, FALSE);
6078     set_field_type(f[n - 1], TYPE_ALPHA, 1);
6079
6080     f[n++] = make_label(2, 20, "First Name");
6081     f[n++] = make_field(3, 20, 1, 12, FALSE);
6082     set_field_type(f[n - 1], TYPE_ALPHA, 1);
6083
6084     f[n++] = make_label(2, 34, "Middle Name");
6085     f[n++] = make_field(3, 34, 1, 12, FALSE);
6086     set_field_type(f[n - 1], fty_middle);
6087
6088     f[n++] = make_label(5, 0, "Comments");
6089     f[n++] = make_field(6, 0, 4, 46, FALSE);
6090
6091     f[n++] = make_label(5, 20, "Password:");
6092     secure =
6093         f[n++] = make_field(5, 30, 1, 9, TRUE);
6094     set_field_type(f[n - 1], fty_passwd);
6095     f[n] = (FIELD *) 0;
6096
6097     if ((form = new_form(f)) != 0) {
6098
6099         display_form(form);
6100
6101         w = form_win(form);
6102         raw();
6103         nonl();                 /* lets us read ^M's */
6104         while (!finished) {
6105             switch (form_driver(form, c = form_virtualize(form, w))) {
6106             case E_OK:
6107                 MvAddStr(5, 57, field_buffer(secure, 1));
6108                 clrtoeol();
6109                 refresh();
6110                 break;
6111             case E_UNKNOWN_COMMAND:
6112                 finished = my_form_driver(form, c);
6113                 break;
6114             default:
6115                 beep();
6116                 break;
6117             }
6118         }
6119
6120         erase_form(form);
6121
6122         free_form(form);
6123     }
6124     for (c = 0; f[c] != 0; c++)
6125         free_field(f[c]);
6126     free_fieldtype(fty_middle);
6127     free_fieldtype(fty_passwd);
6128     noraw();
6129     nl();
6130
6131 #ifdef NCURSES_MOUSE_VERSION
6132     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
6133 #endif
6134 }
6135 #endif /* USE_LIBFORM */
6136
6137 /****************************************************************************
6138  *
6139  * Overlap test
6140  *
6141  ****************************************************************************/
6142
6143 static void
6144 fillwin(WINDOW *win, char ch)
6145 {
6146     int y, x;
6147     int y1, x1;
6148
6149     getmaxyx(win, y1, x1);
6150     for (y = 0; y < y1; y++) {
6151         wmove(win, y, 0);
6152         for (x = 0; x < x1; x++)
6153             waddch(win, UChar(ch));
6154     }
6155 }
6156
6157 static void
6158 crosswin(WINDOW *win, char ch)
6159 {
6160     int y, x;
6161     int y1, x1;
6162
6163     getmaxyx(win, y1, x1);
6164     for (y = 0; y < y1; y++) {
6165         for (x = 0; x < x1; x++)
6166             if (((x > (x1 - 1) / 3) && (x <= (2 * (x1 - 1)) / 3))
6167                 || (((y > (y1 - 1) / 3) && (y <= (2 * (y1 - 1)) / 3)))) {
6168                 wmove(win, y, x);
6169                 waddch(win, UChar(ch));
6170             }
6171     }
6172 }
6173
6174 #define OVERLAP_FLAVORS 5
6175
6176 static void
6177 overlap_helpitem(int state, int item, char *message)
6178 {
6179     int row = (item / 2);
6180     int col = ((item % 2) ? COLS / 2 : 0);
6181
6182     move(LINES - 6 + row, col);
6183     printw("%c%c = %s", state == row ? '>' : ' ', 'a' + item, message);
6184     clrtoeol();
6185 }
6186
6187 static void
6188 overlap_test_1_attr(WINDOW *win, int flavor, int col)
6189 {
6190     short cpair = (short) (1 + (flavor * 2) + col);
6191
6192     switch (flavor) {
6193     case 0:
6194         (void) wattrset(win, A_NORMAL);
6195         break;
6196     case 1:
6197         (void) wattrset(win, A_BOLD);
6198         break;
6199     case 2:
6200         init_pair(cpair, COLOR_BLUE, COLOR_WHITE);
6201         (void) wattrset(win, (int) (COLOR_PAIR(cpair) | A_NORMAL));
6202         break;
6203     case 3:
6204         init_pair(cpair, COLOR_WHITE, COLOR_BLUE);
6205         (void) wattrset(win, (int) (COLOR_PAIR(cpair) | A_BOLD));
6206         break;
6207     }
6208 }
6209
6210 static void
6211 overlap_test_2_attr(WINDOW *win, int flavor, int col)
6212 {
6213     short cpair = (short) (9 + (flavor * 2) + col);
6214
6215     switch (flavor) {
6216     case 0:
6217         /* no effect */
6218         break;
6219     case 1:
6220         /* no effect */
6221         break;
6222     case 2:
6223         init_pair(cpair, COLOR_RED, COLOR_GREEN);
6224         wbkgdset(win, colored_chtype(' ', A_BLINK, cpair));
6225         break;
6226     case 3:
6227         wbkgdset(win, ' ' | A_NORMAL);
6228         break;
6229     }
6230 }
6231
6232 static int
6233 overlap_help(int state, int flavors[OVERLAP_FLAVORS])
6234 {
6235     int row;
6236     int col;
6237     int item;
6238     const char *ths, *tht;
6239     char msg[80];
6240
6241     if (state < 0)
6242         state += OVERLAP_FLAVORS;
6243     state = state % OVERLAP_FLAVORS;
6244     assert(state >= 0 && state < OVERLAP_FLAVORS);
6245
6246     for (item = 0; item < (2 * OVERLAP_FLAVORS); ++item) {
6247         row = item / 2;
6248         col = item % 2;
6249         ths = col ? "B" : "A";
6250         tht = col ? "A" : "B";
6251
6252         switch (row) {
6253         case 0:
6254             flavors[row] = 0;
6255             sprintf(msg, "refresh %s, then %s, then doupdate.", ths, tht);
6256             break;
6257         case 1:
6258             if (use_colors) {
6259                 flavors[row] %= 4;
6260             } else {
6261                 flavors[row] %= 2;
6262             }
6263             overlap_test_1_attr(stdscr, flavors[row], col);
6264             sprintf(msg, "fill window %s with letter %s.", ths, ths);
6265             break;
6266         case 2:
6267             if (use_colors) {
6268                 flavors[row] %= 4;
6269             } else {
6270                 flavors[row] %= 2;
6271             }
6272             switch (flavors[row]) {
6273             case 0:
6274                 sprintf(msg, "cross pattern in window %s.", ths);
6275                 break;
6276             case 1:
6277                 sprintf(msg, "draw box in window %s.", ths);
6278                 break;
6279             case 2:
6280                 sprintf(msg, "set background of window %s.", ths);
6281                 break;
6282             case 3:
6283                 sprintf(msg, "reset background of window %s.", ths);
6284                 break;
6285             }
6286             break;
6287         case 3:
6288             flavors[row] = 0;
6289             sprintf(msg, "clear window %s.", ths);
6290             break;
6291         case 4:
6292             flavors[row] %= 4;
6293             switch (flavors[row]) {
6294             case 0:
6295                 sprintf(msg, "overwrite %s onto %s.", ths, tht);
6296                 break;
6297             case 1:
6298                 sprintf(msg, "copywin(FALSE) %s onto %s.", ths, tht);
6299                 break;
6300             case 2:
6301                 sprintf(msg, "copywin(TRUE) %s onto %s.", ths, tht);
6302                 break;
6303             case 3:
6304                 sprintf(msg, "overlay %s onto %s.", ths, tht);
6305                 break;
6306             }
6307             break;
6308         }
6309         overlap_helpitem(state, item, msg);
6310         (void) wattrset(stdscr, A_NORMAL);
6311         wbkgdset(stdscr, ' ' | A_NORMAL);
6312     }
6313     move(LINES - 1, 0);
6314     printw("^Q/ESC = terminate test.  Up/down/space select test variations (%d %d).",
6315            state, flavors[state]);
6316
6317     return state;
6318 }
6319
6320 static void
6321 overlap_test_0(WINDOW *a, WINDOW *b)
6322 {
6323     touchwin(a);
6324     touchwin(b);
6325     wnoutrefresh(a);
6326     wnoutrefresh(b);
6327     doupdate();
6328 }
6329
6330 static void
6331 overlap_test_1(int flavor, int col, WINDOW *a, char fill)
6332 {
6333     overlap_test_1_attr(a, flavor, col);
6334     fillwin(a, fill);
6335     (void) wattrset(a, A_NORMAL);
6336 }
6337
6338 static void
6339 overlap_test_2(int flavor, int col, WINDOW *a, char fill)
6340 {
6341     overlap_test_2_attr(a, flavor, col);
6342     switch (flavor) {
6343     case 0:
6344         crosswin(a, fill);
6345         break;
6346     case 1:
6347         box(a, 0, 0);
6348         break;
6349     case 2:
6350         /* done in overlap_test_2_attr */
6351         break;
6352     case 3:
6353         /* done in overlap_test_2_attr */
6354         break;
6355     }
6356 }
6357
6358 static void
6359 overlap_test_3(WINDOW *a)
6360 {
6361     wclear(a);
6362     wmove(a, 0, 0);
6363 }
6364
6365 static void
6366 overlap_test_4(int flavor, WINDOW *a, WINDOW *b)
6367 {
6368     switch (flavor) {
6369     case 0:
6370         overwrite(a, b);
6371         break;
6372     case 1:
6373         copywin(a, b, 0, 0, 0, 0, getmaxy(b), getmaxx(b), FALSE);
6374         break;
6375     case 2:
6376         copywin(a, b, 0, 0, 0, 0, getmaxy(b), getmaxx(b), TRUE);
6377         break;
6378     case 3:
6379         overlay(a, b);
6380         break;
6381     }
6382 }
6383
6384 /* test effects of overlapping windows */
6385 static void
6386 overlap_test(void)
6387 {
6388     int ch;
6389     int state, flavor[OVERLAP_FLAVORS];
6390
6391     WINDOW *win1 = newwin(9, 20, 3, 3);
6392     WINDOW *win2 = newwin(9, 20, 9, 16);
6393
6394     curs_set(0);
6395     raw();
6396     refresh();
6397     move(0, 0);
6398     printw("This test shows the behavior of wnoutrefresh() with respect to\n");
6399     printw("the shared region of two overlapping windows A and B.  The cross\n");
6400     printw("pattern in each window does not overlap the other.\n");
6401
6402     memset(flavor, 0, sizeof(flavor));
6403     state = overlap_help(0, flavor);
6404
6405     while (!isQuit(ch = Getchar()))
6406         switch (ch) {
6407         case 'a':               /* refresh window A first, then B */
6408             overlap_test_0(win1, win2);
6409             break;
6410
6411         case 'b':               /* refresh window B first, then A */
6412             overlap_test_0(win2, win1);
6413             break;
6414
6415         case 'c':               /* fill window A so it's visible */
6416             overlap_test_1(flavor[1], 0, win1, 'A');
6417             break;
6418
6419         case 'd':               /* fill window B so it's visible */
6420             overlap_test_1(flavor[1], 1, win2, 'B');
6421             break;
6422
6423         case 'e':               /* cross test pattern in window A */
6424             overlap_test_2(flavor[2], 0, win1, 'A');
6425             break;
6426
6427         case 'f':               /* cross test pattern in window A */
6428             overlap_test_2(flavor[2], 1, win2, 'B');
6429             break;
6430
6431         case 'g':               /* clear window A */
6432             overlap_test_3(win1);
6433             break;
6434
6435         case 'h':               /* clear window B */
6436             overlap_test_3(win2);
6437             break;
6438
6439         case 'i':               /* overwrite A onto B */
6440             overlap_test_4(flavor[4], win1, win2);
6441             break;
6442
6443         case 'j':               /* overwrite B onto A */
6444             overlap_test_4(flavor[4], win2, win1);
6445             break;
6446
6447         case CTRL('n'):
6448         case KEY_DOWN:
6449             state = overlap_help(state + 1, flavor);
6450             break;
6451
6452         case CTRL('p'):
6453         case KEY_UP:
6454             state = overlap_help(state - 1, flavor);
6455             break;
6456
6457         case ' ':
6458             flavor[state] += 1;
6459             state = overlap_help(state, flavor);
6460             break;
6461
6462         case '?':
6463             state = overlap_help(state, flavor);
6464             break;
6465
6466         default:
6467             beep();
6468             break;
6469         }
6470
6471     delwin(win2);
6472     delwin(win1);
6473     erase();
6474     curs_set(1);
6475     endwin();
6476 }
6477
6478 /****************************************************************************
6479  *
6480  * Main sequence
6481  *
6482  ****************************************************************************/
6483
6484 static bool
6485 do_single_test(const char c)
6486 /* perform a single specified test */
6487 {
6488     switch (c) {
6489     case 'a':
6490         getch_test();
6491         break;
6492
6493 #if USE_WIDEC_SUPPORT
6494     case 'A':
6495         get_wch_test();
6496         break;
6497 #endif
6498
6499     case 'b':
6500         attr_test();
6501         break;
6502
6503 #if USE_WIDEC_SUPPORT
6504     case 'B':
6505         wide_attr_test();
6506         break;
6507 #endif
6508
6509     case 'c':
6510         if (!use_colors)
6511             Cannot("does not support color.");
6512         else
6513             color_test();
6514         break;
6515
6516 #if USE_WIDEC_SUPPORT
6517     case 'C':
6518         if (!use_colors)
6519             Cannot("does not support color.");
6520         else
6521             wide_color_test();
6522         break;
6523 #endif
6524
6525     case 'd':
6526         if (!use_colors)
6527             Cannot("does not support color.");
6528         else if (!can_change_color())
6529             Cannot("has hardwired color values.");
6530         else
6531             color_edit();
6532         break;
6533
6534 #if USE_SOFTKEYS
6535     case 'e':
6536         slk_test();
6537         break;
6538
6539 #if USE_WIDEC_SUPPORT
6540     case 'E':
6541         wide_slk_test();
6542         break;
6543 #endif
6544 #endif
6545
6546     case 'f':
6547         acs_display();
6548         break;
6549
6550 #if USE_WIDEC_SUPPORT
6551     case 'F':
6552         wide_acs_display();
6553         break;
6554 #endif
6555
6556 #if USE_LIBPANEL
6557     case 'o':
6558         demo_panels(init_panel, fill_panel);
6559         break;
6560 #endif
6561
6562 #if USE_WIDEC_SUPPORT && USE_LIBPANEL
6563     case 'O':
6564         demo_panels(init_wide_panel, fill_wide_panel);
6565         break;
6566 #endif
6567
6568     case 'g':
6569         acs_and_scroll();
6570         break;
6571
6572     case 'i':
6573         flushinp_test(stdscr);
6574         break;
6575
6576     case 'k':
6577         test_sgr_attributes();
6578         break;
6579
6580 #if USE_LIBMENU
6581     case 'm':
6582         menu_test();
6583         break;
6584 #endif
6585
6586     case 'p':
6587         demo_pad();
6588         break;
6589
6590 #if USE_LIBFORM
6591     case 'r':
6592         demo_forms();
6593         break;
6594 #endif
6595
6596     case 's':
6597         overlap_test();
6598         break;
6599
6600 #if USE_LIBMENU && defined(TRACE)
6601     case 't':
6602         trace_set();
6603         break;
6604 #endif
6605
6606     case '?':
6607         break;
6608
6609     default:
6610         return FALSE;
6611     }
6612
6613     return TRUE;
6614 }
6615
6616 static void
6617 usage(void)
6618 {
6619     static const char *const tbl[] =
6620     {
6621         "Usage: ncurses [options]"
6622         ,""
6623         ,"Options:"
6624 #ifdef NCURSES_VERSION
6625         ,"  -a f,b   set default-colors (assumed white-on-black)"
6626         ,"  -d       use default-colors if terminal supports them"
6627 #endif
6628         ,"  -E       call use_env(FALSE) to ignore $LINES and $COLUMNS"
6629 #if USE_SOFTKEYS
6630         ,"  -e fmt   specify format for soft-keys test (e)"
6631 #endif
6632 #if HAVE_RIPOFFLINE
6633         ,"  -f       rip-off footer line (can repeat)"
6634         ,"  -h       rip-off header line (can repeat)"
6635 #endif
6636         ,"  -m       do not use colors"
6637         ,"  -p file  rgb values to use in 'd' rather than ncurses's builtin"
6638 #if USE_LIBPANEL
6639         ,"  -s msec  specify nominal time for panel-demo (default: 1, to hold)"
6640 #endif
6641 #if defined(NCURSES_VERSION_PATCH) && (NCURSES_VERSION_PATCH >= 20120714)
6642         ,"  -T       call use_tioctl(TRUE) to allow SIGWINCH to override environment"
6643 #endif
6644 #ifdef TRACE
6645         ,"  -t mask  specify default trace-level (may toggle with ^T)"
6646 #endif
6647     };
6648     size_t n;
6649     for (n = 0; n < SIZEOF(tbl); n++)
6650         fprintf(stderr, "%s\n", tbl[n]);
6651     ExitProgram(EXIT_FAILURE);
6652 }
6653
6654 static void
6655 set_terminal_modes(void)
6656 {
6657     noraw();
6658     cbreak();
6659     noecho();
6660     scrollok(stdscr, TRUE);
6661     idlok(stdscr, TRUE);
6662     keypad(stdscr, TRUE);
6663 }
6664
6665 #ifdef SIGUSR1
6666 static void
6667 announce_sig(int sig)
6668 {
6669     (void) fprintf(stderr, "Handled signal %d\r\n", sig);
6670 }
6671 #endif
6672
6673 #if HAVE_RIPOFFLINE
6674 static int
6675 rip_footer(WINDOW *win, int cols)
6676 {
6677     wbkgd(win, A_REVERSE);
6678     werase(win);
6679     wmove(win, 0, 0);
6680     wprintw(win, "footer: window %p, %d columns", (void *) win, cols);
6681     wnoutrefresh(win);
6682     return OK;
6683 }
6684
6685 static int
6686 rip_header(WINDOW *win, int cols)
6687 {
6688     wbkgd(win, A_REVERSE);
6689     werase(win);
6690     wmove(win, 0, 0);
6691     wprintw(win, "header: window %p, %d columns", (void *) win, cols);
6692     wnoutrefresh(win);
6693     return OK;
6694 }
6695 #endif /* HAVE_RIPOFFLINE */
6696
6697 static void
6698 main_menu(bool top)
6699 {
6700     char command;
6701
6702     do {
6703         (void) puts("This is the ncurses main menu");
6704         (void) puts("a = keyboard and mouse input test");
6705 #if USE_WIDEC_SUPPORT
6706         (void) puts("A = wide-character keyboard and mouse input test");
6707 #endif
6708         (void) puts("b = character attribute test");
6709 #if USE_WIDEC_SUPPORT
6710         (void) puts("B = wide-character attribute test");
6711 #endif
6712         (void) puts("c = color test pattern");
6713 #if USE_WIDEC_SUPPORT
6714         (void) puts("C = color test pattern using wide-character calls");
6715 #endif
6716         if (top)
6717             (void) puts("d = edit RGB color values");
6718 #if USE_SOFTKEYS
6719         (void) puts("e = exercise soft keys");
6720 #if USE_WIDEC_SUPPORT
6721         (void) puts("E = exercise soft keys using wide-characters");
6722 #endif
6723 #endif
6724         (void) puts("f = display ACS characters");
6725 #if USE_WIDEC_SUPPORT
6726         (void) puts("F = display Wide-ACS characters");
6727 #endif
6728         (void) puts("g = display windows and scrolling");
6729         (void) puts("i = test of flushinp()");
6730         (void) puts("k = display character attributes");
6731 #if USE_LIBMENU
6732         (void) puts("m = menu code test");
6733 #endif
6734 #if USE_LIBPANEL
6735         (void) puts("o = exercise panels library");
6736 #if USE_WIDEC_SUPPORT
6737         (void) puts("O = exercise panels with wide-characters");
6738 #endif
6739 #endif
6740         (void) puts("p = exercise pad features");
6741         (void) puts("q = quit");
6742 #if USE_LIBFORM
6743         (void) puts("r = exercise forms code");
6744 #endif
6745         (void) puts("s = overlapping-refresh test");
6746 #if USE_LIBMENU && defined(TRACE)
6747         (void) puts("t = set trace level");
6748 #endif
6749         (void) puts("? = repeat this command summary");
6750
6751         (void) fputs("> ", stdout);
6752         (void) fflush(stdout);  /* necessary under SVr4 curses */
6753
6754         /*
6755          * This used to be an 'fgets()' call (until 1996/10).  However with
6756          * some runtime libraries, mixing stream I/O and 'read()' causes the
6757          * input stream to be flushed when switching between the two.
6758          */
6759         command = 0;
6760         for (;;) {
6761             char ch = '\0';
6762             if (read(fileno(stdin), &ch, 1) <= 0) {
6763                 if (command == 0)
6764                     command = 'q';
6765                 break;
6766             } else if (command == 0 && !isspace(UChar(ch))) {
6767                 command = ch;
6768             } else if (ch == '\n' || ch == '\r') {
6769                 if ((command == 'd') && !top) {
6770                     (void) fputs("Do not nest test-d\n", stdout);
6771                     command = 0;
6772                 }
6773                 if (command != 0)
6774                     break;
6775                 (void) fputs("> ", stdout);
6776                 (void) fflush(stdout);
6777             }
6778         }
6779
6780         if (do_single_test(command)) {
6781             /*
6782              * This may be overkill; it's intended to reset everything back
6783              * to the initial terminal modes so that tests don't get in
6784              * each other's way.
6785              */
6786             flushinp();
6787             set_terminal_modes();
6788             reset_prog_mode();
6789             clear();
6790             refresh();
6791             endwin();
6792             if (command == '?') {
6793                 (void) puts("This is the ncurses capability tester.");
6794                 (void)
6795                     puts("You may select a test from the main menu by typing the");
6796                 (void)
6797                     puts("key letter of the choice (the letter to left of the =)");
6798                 (void)
6799                     puts("at the > prompt.  Type `q' to exit.");
6800             }
6801             continue;
6802         }
6803     } while
6804         (command != 'q');
6805 }
6806
6807 /*+-------------------------------------------------------------------------
6808         main(argc,argv)
6809 --------------------------------------------------------------------------*/
6810
6811 #define okCOLOR(n) ((n) >= 0 && (n) < max_colors)
6812 #define okRGB(n)   ((n) >= 0 && (n) <= 1000)
6813
6814 int
6815 main(int argc, char *argv[])
6816 {
6817     int c;
6818     int my_e_param = 1;
6819 #ifdef NCURSES_VERSION
6820     int default_fg = COLOR_WHITE;
6821     int default_bg = COLOR_BLACK;
6822     bool assumed_colors = FALSE;
6823     bool default_colors = FALSE;
6824 #endif
6825     char *palette_file = 0;
6826     bool monochrome = FALSE;
6827
6828     setlocale(LC_ALL, "");
6829
6830     while ((c = getopt(argc, argv, "a:dEe:fhmp:s:Tt:")) != -1) {
6831         switch (c) {
6832 #ifdef NCURSES_VERSION
6833         case 'a':
6834             assumed_colors = TRUE;
6835             switch (sscanf(optarg, "%d,%d", &default_fg, &default_bg)) {
6836             case 0:
6837                 default_fg = COLOR_WHITE;
6838                 /* FALLTHRU */
6839             case 1:
6840                 default_bg = COLOR_BLACK;
6841                 break;
6842             }
6843             break;
6844         case 'd':
6845             default_colors = TRUE;
6846             break;
6847 #endif
6848         case 'E':
6849             use_env(FALSE);
6850             break;
6851         case 'e':
6852             my_e_param = atoi(optarg);
6853 #ifdef NCURSES_VERSION
6854             if (my_e_param > 3) /* allow extended layouts */
6855                 usage();
6856 #else
6857             if (my_e_param > 1)
6858                 usage();
6859 #endif
6860             break;
6861 #if HAVE_RIPOFFLINE
6862         case 'f':
6863             ripoffline(-1, rip_footer);
6864             break;
6865         case 'h':
6866             ripoffline(1, rip_header);
6867             break;
6868 #endif /* HAVE_RIPOFFLINE */
6869         case 'm':
6870             monochrome = TRUE;
6871             break;
6872         case 'p':
6873             palette_file = optarg;
6874             break;
6875 #if USE_LIBPANEL
6876         case 's':
6877             nap_msec = (int) atol(optarg);
6878             break;
6879 #endif
6880 #if defined(NCURSES_VERSION_PATCH) && (NCURSES_VERSION_PATCH >= 20120714)
6881         case 'T':
6882             use_tioctl(TRUE);
6883             break;
6884 #endif
6885 #ifdef TRACE
6886         case 't':
6887             save_trace = (unsigned) strtol(optarg, 0, 0);
6888             break;
6889 #endif
6890         default:
6891             usage();
6892         }
6893     }
6894
6895     /*
6896      * If there's no menus (unlikely for ncurses!), then we'll have to set
6897      * tracing on initially, just in case the user wants to test something that
6898      * doesn't involve wGetchar.
6899      */
6900 #ifdef TRACE
6901     /* enable debugging */
6902 #if !USE_LIBMENU
6903     trace(save_trace);
6904 #else
6905     if (!isatty(fileno(stdin)))
6906         trace(save_trace);
6907 #endif /* USE_LIBMENU */
6908 #endif /* TRACE */
6909
6910 #if USE_SOFTKEYS
6911     /* tell it we're going to play with soft keys */
6912     slk_init(my_e_param);
6913 #endif
6914
6915 #ifdef SIGUSR1
6916     /* set up null signal catcher so we can see what interrupts to getch do */
6917     signal(SIGUSR1, announce_sig);
6918 #endif
6919
6920     /* we must initialize the curses data structure only once */
6921     initscr();
6922     bkgdset(BLANK);
6923
6924     /* tests, in general, will want these modes */
6925     use_colors = (bool) (monochrome ? FALSE : has_colors());
6926
6927     if (use_colors) {
6928         start_color();
6929 #ifdef NCURSES_VERSION_PATCH
6930         max_colors = COLORS;    /* was > 16 ? 16 : COLORS */
6931 #if HAVE_USE_DEFAULT_COLORS
6932         if (default_colors) {
6933             use_default_colors();
6934             min_colors = -1;
6935         }
6936 #if HAVE_ASSUME_DEFAULT_COLORS
6937         if (assumed_colors)
6938             assume_default_colors(default_fg, default_bg);
6939 #endif
6940 #endif
6941 #else /* normal SVr4 curses */
6942         max_colors = COLORS;    /* was > 8 ? 8 : COLORS */
6943 #endif
6944         max_pairs = COLOR_PAIRS;        /* was > 256 ? 256 : COLOR_PAIRS */
6945
6946         if (can_change_color()) {
6947             short cp;
6948             all_colors = typeMalloc(RGB_DATA, (unsigned) max_colors);
6949             if (!all_colors)
6950                 failed("all_colors");
6951             for (cp = 0; cp < max_colors; ++cp) {
6952                 color_content(cp,
6953                               &all_colors[cp].red,
6954                               &all_colors[cp].green,
6955                               &all_colors[cp].blue);
6956             }
6957             if (palette_file != 0) {
6958                 FILE *fp = fopen(palette_file, "r");
6959                 if (fp != 0) {
6960                     char buffer[BUFSIZ];
6961                     int red, green, blue;
6962                     int scale = 1000;
6963                     while (fgets(buffer, sizeof(buffer), fp) != 0) {
6964                         if (sscanf(buffer, "scale:%d", &c) == 1) {
6965                             scale = c;
6966                         } else if (sscanf(buffer, "%d:%d %d %d",
6967                                           &c,
6968                                           &red,
6969                                           &green,
6970                                           &blue) == 4
6971                                    && okCOLOR(c)
6972                                    && okRGB(red)
6973                                    && okRGB(green)
6974                                    && okRGB(blue)) {
6975                             all_colors[c].red = (short) ((red * 1000) / scale);
6976                             all_colors[c].green = (short) ((green * 1000) / scale);
6977                             all_colors[c].blue = (short) ((blue * 1000) / scale);
6978                         }
6979                     }
6980                     fclose(fp);
6981                 }
6982             }
6983         }
6984     }
6985     set_terminal_modes();
6986     def_prog_mode();
6987
6988     /*
6989      * Return to terminal mode, so we're guaranteed of being able to
6990      * select terminal commands even if the capabilities are wrong.
6991      */
6992     endwin();
6993
6994 #if HAVE_CURSES_VERSION
6995     (void) printf("Welcome to %s.  Press ? for help.\n", curses_version());
6996 #elif defined(NCURSES_VERSION_MAJOR) && defined(NCURSES_VERSION_MINOR) && defined(NCURSES_VERSION_PATCH)
6997     (void) printf("Welcome to ncurses %d.%d.%d.  Press ? for help.\n",
6998                   NCURSES_VERSION_MAJOR,
6999                   NCURSES_VERSION_MINOR,
7000                   NCURSES_VERSION_PATCH);
7001 #else
7002     (void) puts("Welcome to ncurses.  Press ? for help.");
7003 #endif
7004
7005     main_menu(TRUE);
7006
7007     ExitProgram(EXIT_SUCCESS);
7008 }
7009
7010 /* ncurses.c ends here */