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