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