ncurses 5.6 - patch 20080105
[ncurses.git] / test / ncurses.c
1 /****************************************************************************
2  * Copyright (c) 1998-2006,2007 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.296 2007/12/29 21:05:43 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_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 #ifdef CURSES_WACS_ARRAY
110 #define WACS_S3         (&(CURSES_WACS_ARRAY['p']))     /* scan line 3 */
111 #define WACS_S7         (&(CURSES_WACS_ARRAY['r']))     /* scan line 7 */
112 #define WACS_LEQUAL     (&(CURSES_WACS_ARRAY['y']))     /* less/equal */
113 #define WACS_GEQUAL     (&(CURSES_WACS_ARRAY['z']))     /* greater/equal */
114 #define WACS_PI         (&(CURSES_WACS_ARRAY['{']))     /* Pi */
115 #define WACS_NEQUAL     (&(CURSES_WACS_ARRAY['|']))     /* not equal */
116 #define WACS_STERLING   (&(CURSES_WACS_ARRAY['}']))     /* UK pound sign */
117 #endif
118
119 #endif
120
121 #define P(string)       printw("%s\n", string)
122
123 #define BLANK           ' '     /* this is the background character */
124
125 #undef max_colors
126 static int max_colors;          /* the actual number of colors we'll use */
127 static int min_colors;          /* the minimum color code */
128 static bool use_colors;         /* true if we use colors */
129
130 #undef max_pairs
131 static int max_pairs;           /* ...and the number of color pairs */
132
133 typedef struct {
134     short red;
135     short green;
136     short blue;
137 } RGB_DATA;
138
139 static RGB_DATA *all_colors;
140
141 static void main_menu(bool);
142
143 /* The behavior of mvhline, mvvline for negative/zero length is unspecified,
144  * though we can rely on negative x/y values to stop the macro.
145  */
146 static void
147 do_h_line(int y, int x, chtype c, int to)
148 {
149     if ((to) > (x))
150         mvhline(y, x, c, (to) - (x));
151 }
152
153 static void
154 do_v_line(int y, int x, chtype c, int to)
155 {
156     if ((to) > (y))
157         mvvline(y, x, c, (to) - (y));
158 }
159
160 static void
161 Repaint(void)
162 {
163     touchwin(stdscr);
164     touchwin(curscr);
165     wrefresh(curscr);
166 }
167
168 static bool
169 isQuit(int c)
170 {
171     return ((c) == QUIT || (c) == ESCAPE);
172 }
173 #define case_QUIT       case QUIT: case ESCAPE
174
175 /* Common function to allow ^T to toggle trace-mode in the middle of a test
176  * so that trace-files can be made smaller.
177  */
178 static int
179 wGetchar(WINDOW *win)
180 {
181     int c;
182 #ifdef TRACE
183     while ((c = wgetch(win)) == CTRL('T')) {
184         if (_nc_tracing) {
185             save_trace = _nc_tracing;
186             _tracef("TOGGLE-TRACING OFF");
187             _nc_tracing = 0;
188         } else {
189             _nc_tracing = save_trace;
190         }
191         trace(_nc_tracing);
192         if (_nc_tracing)
193             _tracef("TOGGLE-TRACING ON");
194     }
195 #else
196     c = wgetch(win);
197 #endif
198     return c;
199 }
200 #define Getchar() wGetchar(stdscr)
201
202 /* replaces wgetnstr(), since we want to be able to edit values */
203 static void
204 wGetstring(WINDOW *win, char *buffer, int limit)
205 {
206     int y0, x0, x, ch;
207     bool done = FALSE;
208
209     echo();
210     getyx(win, y0, x0);
211     wattrset(win, A_REVERSE);
212
213     x = strlen(buffer);
214     while (!done) {
215         if (x > (int) strlen(buffer))
216             x = (int) strlen(buffer);
217         wmove(win, y0, x0);
218         wprintw(win, "%-*s", limit, buffer);
219         wmove(win, y0, x0 + x);
220         switch (ch = wGetchar(win)) {
221         case '\n':
222         case KEY_ENTER:
223             done = TRUE;
224             break;
225         case CTRL('U'):
226             *buffer = '\0';
227             break;
228         case '\b':
229         case KEY_BACKSPACE:
230         case KEY_DC:
231             if (x > 0) {
232                 int j;
233                 for (j = --x; (buffer[j] = buffer[j + 1]) != '\0'; ++j) {
234                     ;
235                 }
236             } else {
237                 beep();
238             }
239             break;
240         case KEY_LEFT:
241             if (x > 0) {
242                 --x;
243             } else {
244                 flash();
245             }
246             break;
247         case KEY_RIGHT:
248             ++x;
249             break;
250         default:
251             if (!isprint(ch) || ch >= KEY_MIN) {
252                 beep();
253             } else if ((int) strlen(buffer) < limit) {
254                 int j;
255                 for (j = strlen(buffer) + 1; j > x; --j) {
256                     buffer[j] = buffer[j - 1];
257                 }
258                 buffer[x++] = ch;
259             } else {
260                 flash();
261             }
262         }
263     }
264
265     wattroff(win, A_REVERSE);
266     wmove(win, y0, x0);
267     noecho();
268 }
269
270 #if USE_WIDEC_SUPPORT
271 static int
272 wGet_wchar(WINDOW *win, wint_t *result)
273 {
274     int c;
275 #ifdef TRACE
276     while ((c = wget_wch(win, result)) == CTRL('T')) {
277         if (_nc_tracing) {
278             save_trace = _nc_tracing;
279             _tracef("TOGGLE-TRACING OFF");
280             _nc_tracing = 0;
281         } else {
282             _nc_tracing = save_trace;
283         }
284         trace(_nc_tracing);
285         if (_nc_tracing)
286             _tracef("TOGGLE-TRACING ON");
287     }
288 #else
289     c = wget_wch(win, result);
290 #endif
291     return c;
292 }
293 #define Get_wchar(result) wGet_wchar(stdscr, result)
294
295 /* replaces wgetn_wstr(), since we want to be able to edit values */
296 static void
297 wGet_wstring(WINDOW *win, wchar_t *buffer, int limit)
298 {
299     int y0, x0, x;
300     wint_t ch;
301     bool done = FALSE;
302     bool fkey = FALSE;
303
304     echo();
305     getyx(win, y0, x0);
306     wattrset(win, A_REVERSE);
307
308     x = wcslen(buffer);
309     while (!done) {
310         if (x > (int) wcslen(buffer))
311             x = (int) wcslen(buffer);
312
313         /* clear the "window' */
314         wmove(win, y0, x0);
315         wprintw(win, "%*s", limit, " ");
316
317         /* write the existing buffer contents */
318         wmove(win, y0, x0);
319         waddnwstr(win, buffer, limit);
320
321         /* positions the cursor past character 'x' */
322         wmove(win, y0, x0);
323         waddnwstr(win, buffer, x);
324
325         switch (wGet_wchar(win, &ch)) {
326         case KEY_CODE_YES:
327             fkey = TRUE;
328             switch (ch) {
329             case KEY_ENTER:
330                 ch = '\n';
331                 fkey = FALSE;
332                 break;
333             case KEY_BACKSPACE:
334             case KEY_DC:
335                 ch = '\b';
336                 fkey = FALSE;
337                 break;
338             case KEY_LEFT:
339             case KEY_RIGHT:
340                 break;
341             default:
342                 ch = (wint_t) -1;
343                 break;
344             }
345             break;
346         case OK:
347             fkey = FALSE;
348             break;
349         default:
350             ch = (wint_t) -1;
351             fkey = TRUE;
352             break;
353         }
354
355         switch (ch) {
356         case '\n':
357             done = TRUE;
358             break;
359         case CTRL('U'):
360             *buffer = '\0';
361             break;
362         case '\b':
363             if (x > 0) {
364                 int j;
365                 for (j = --x; (buffer[j] = buffer[j + 1]) != '\0'; ++j) {
366                     ;
367                 }
368             } else {
369                 beep();
370             }
371             break;
372         case KEY_LEFT:
373             if (x > 0) {
374                 --x;
375             } else {
376                 beep();
377             }
378             break;
379         case KEY_RIGHT:
380             ++x;
381             break;
382         default:
383             if (fkey) {
384                 beep();
385             } else if ((int) wcslen(buffer) < limit) {
386                 int j;
387                 for (j = wcslen(buffer) + 1; j > x; --j) {
388                     buffer[j] = buffer[j - 1];
389                 }
390                 buffer[x++] = ch;
391             } else {
392                 beep();
393             }
394         }
395     }
396
397     wattroff(win, A_REVERSE);
398     wmove(win, y0, x0);
399     noecho();
400 }
401
402 #endif
403
404 static void
405 Pause(void)
406 {
407     move(LINES - 1, 0);
408     addstr("Press any key to continue... ");
409     (void) Getchar();
410 }
411
412 static void
413 Cannot(const char *what)
414 {
415     printw("\nThis %s terminal %s\n\n", getenv("TERM"), what);
416     Pause();
417 }
418
419 static void
420 ShellOut(bool message)
421 {
422     if (message)
423         addstr("Shelling out...");
424     def_prog_mode();
425     endwin();
426     system("sh");
427     if (message)
428         addstr("returned from shellout.\n");
429     refresh();
430 }
431
432 #ifdef NCURSES_MOUSE_VERSION
433 /*
434  * This function is the same as _tracemouse(), but we cannot count on that
435  * being available in the non-debug library.
436  */
437 static const char *
438 mouse_decode(MEVENT const *ep)
439 {
440     static char buf[80 + (5 * 10) + (32 * 15)];
441
442     (void) sprintf(buf, "id %2d  at (%2d, %2d, %2d) state %4lx = {",
443                    ep->id, ep->x, ep->y, ep->z, (unsigned long) ep->bstate);
444
445 #define SHOW(m, s) if ((ep->bstate & m)==m) {strcat(buf,s); strcat(buf, ", ");}
446
447     SHOW(BUTTON1_RELEASED, "release-1");
448     SHOW(BUTTON1_PRESSED, "press-1");
449     SHOW(BUTTON1_CLICKED, "click-1");
450     SHOW(BUTTON1_DOUBLE_CLICKED, "doubleclick-1");
451     SHOW(BUTTON1_TRIPLE_CLICKED, "tripleclick-1");
452 #if NCURSES_MOUSE_VERSION == 1
453     SHOW(BUTTON1_RESERVED_EVENT, "reserved-1");
454 #endif
455
456     SHOW(BUTTON2_RELEASED, "release-2");
457     SHOW(BUTTON2_PRESSED, "press-2");
458     SHOW(BUTTON2_CLICKED, "click-2");
459     SHOW(BUTTON2_DOUBLE_CLICKED, "doubleclick-2");
460     SHOW(BUTTON2_TRIPLE_CLICKED, "tripleclick-2");
461 #if NCURSES_MOUSE_VERSION == 1
462     SHOW(BUTTON2_RESERVED_EVENT, "reserved-2");
463 #endif
464
465     SHOW(BUTTON3_RELEASED, "release-3");
466     SHOW(BUTTON3_PRESSED, "press-3");
467     SHOW(BUTTON3_CLICKED, "click-3");
468     SHOW(BUTTON3_DOUBLE_CLICKED, "doubleclick-3");
469     SHOW(BUTTON3_TRIPLE_CLICKED, "tripleclick-3");
470 #if NCURSES_MOUSE_VERSION == 1
471     SHOW(BUTTON3_RESERVED_EVENT, "reserved-3");
472 #endif
473
474     SHOW(BUTTON4_RELEASED, "release-4");
475     SHOW(BUTTON4_PRESSED, "press-4");
476     SHOW(BUTTON4_CLICKED, "click-4");
477     SHOW(BUTTON4_DOUBLE_CLICKED, "doubleclick-4");
478     SHOW(BUTTON4_TRIPLE_CLICKED, "tripleclick-4");
479 #if NCURSES_MOUSE_VERSION == 1
480     SHOW(BUTTON4_RESERVED_EVENT, "reserved-4");
481 #endif
482
483 #if NCURSES_MOUSE_VERSION == 2
484     SHOW(BUTTON5_RELEASED, "release-5");
485     SHOW(BUTTON5_PRESSED, "press-5");
486     SHOW(BUTTON5_CLICKED, "click-5");
487     SHOW(BUTTON5_DOUBLE_CLICKED, "doubleclick-5");
488     SHOW(BUTTON5_TRIPLE_CLICKED, "tripleclick-5");
489 #endif
490
491     SHOW(BUTTON_CTRL, "ctrl");
492     SHOW(BUTTON_SHIFT, "shift");
493     SHOW(BUTTON_ALT, "alt");
494     SHOW(ALL_MOUSE_EVENTS, "all-events");
495     SHOW(REPORT_MOUSE_POSITION, "position");
496
497 #undef SHOW
498
499     if (buf[strlen(buf) - 1] == ' ')
500         buf[strlen(buf) - 2] = '\0';
501     (void) strcat(buf, "}");
502     return (buf);
503 }
504 #endif /* NCURSES_MOUSE_VERSION */
505
506 /****************************************************************************
507  *
508  * Character input test
509  *
510  ****************************************************************************/
511
512 static void
513 setup_getch(WINDOW *win, bool flags[])
514 {
515     keypad(win, flags['k']);    /* should be redundant, but for testing */
516     meta(win, flags['m']);      /* force this to a known state */
517     if (flags['e'])
518         echo();
519     else
520         noecho();
521 }
522
523 static void
524 wgetch_help(WINDOW *win, bool flags[])
525 {
526     static const char *help[] =
527     {
528         "e  -- toggle echo mode"
529         ,"g  -- triggers a getstr test"
530         ,"k  -- toggle keypad/literal mode"
531         ,"m  -- toggle meta (7-bit/8-bit) mode"
532         ,"^q -- quit"
533         ,"s  -- shell out\n"
534         ,"w  -- create a new window"
535 #ifdef SIGTSTP
536         ,"z  -- suspend this process"
537 #endif
538     };
539     int y, x;
540     unsigned chk = ((SIZEOF(help) + 1) / 2);
541     unsigned n;
542
543     getyx(win, y, x);
544     move(0, 0);
545     printw("Type any key to see its %s value.  Also:\n",
546            flags['k'] ? "keypad" : "literal");
547     for (n = 0; n < SIZEOF(help); ++n) {
548         int row = 1 + (n % chk);
549         int col = (n >= chk) ? COLS / 2 : 0;
550         int flg = ((strstr(help[n], "toggle") != 0)
551                    && (flags[UChar(*help[n])] != FALSE));
552         if (flg)
553             standout();
554         mvprintw(row, col, "%s", help[n]);
555         if (col == 0)
556             clrtoeol();
557         if (flg)
558             standend();
559     }
560     wrefresh(stdscr);
561     wmove(win, y, x);
562 }
563
564 static void
565 wgetch_wrap(WINDOW *win, int first_y)
566 {
567     int last_y = getmaxy(win) - 1;
568     int y = getcury(win) + 1;
569
570     if (y >= last_y)
571         y = first_y;
572     wmove(win, y, 0);
573     wclrtoeol(win);
574 }
575
576 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
577 typedef struct {
578     WINDOW *text;
579     WINDOW *frame;
580 } WINSTACK;
581
582 static WINSTACK *winstack = 0;
583 static unsigned len_winstack = 0;
584
585 static void
586 forget_boxes(void)
587 {
588     if (winstack != 0) {
589         free(winstack);
590     }
591     winstack = 0;
592     len_winstack = 0;
593 }
594
595 static void
596 remember_boxes(unsigned level, WINDOW *txt_win, WINDOW *box_win)
597 {
598     unsigned need = (level + 1) * 2;
599
600     if (winstack == 0) {
601         len_winstack = 20;
602         winstack = (WINSTACK *) malloc(len_winstack * sizeof(WINSTACK));
603     } else if (need >= len_winstack) {
604         len_winstack = need;
605         winstack = (WINSTACK *) realloc(winstack, len_winstack * sizeof(WINSTACK));
606     }
607     winstack[level].text = txt_win;
608     winstack[level].frame = box_win;
609 }
610
611 #if USE_SOFTKEYS && (NCURSES_VERSION_PATCH < 20071229)
612 static void
613 slk_repaint(void)
614 {
615     /* this chunk is now done in resize_term() */
616     slk_touch();
617     slk_clear();
618     slk_noutrefresh();
619 }
620
621 #else
622 #define slk_repaint()           /* nothing */
623 #endif
624
625 /*
626  * For wgetch_test(), we create pairs of windows - one for a box, one for text.
627  * Resize both and paint the box in the parent.
628  */
629 static void
630 resize_boxes(unsigned level, WINDOW *win)
631 {
632     unsigned n;
633     int base = 5;
634     int high = LINES - base;
635     int wide = COLS;
636
637     touchwin(stdscr);
638     wnoutrefresh(stdscr);
639
640     slk_repaint();
641
642     for (n = 0; n < level; ++n) {
643         wresize(winstack[n].frame, high, wide);
644         wresize(winstack[n].text, high - 2, wide - 2);
645         high -= 2;
646         wide -= 2;
647         werase(winstack[n].text);
648         box(winstack[n].frame, 0, 0);
649         wnoutrefresh(winstack[n].frame);
650         wprintw(winstack[n].text,
651                 "size %dx%d\n",
652                 getmaxy(winstack[n].text),
653                 getmaxx(winstack[n].text));
654         wnoutrefresh(winstack[n].text);
655         if (winstack[n].text == win)
656             break;
657     }
658     doupdate();
659 }
660 #else
661 #define forget_boxes()          /* nothing */
662 #define remember_boxes(level,text,frame)        /* nothing */
663 #endif
664
665 static void
666 wgetch_test(unsigned level, WINDOW *win, int delay)
667 {
668     char buf[BUFSIZ];
669     int first_y, first_x;
670     int c;
671     int incount = 0;
672     bool flags[256];
673     bool blocking = (delay < 0);
674
675     memset(flags, FALSE, sizeof(flags));
676     flags[UChar('k')] = (win == stdscr);
677
678     setup_getch(win, flags);
679     wtimeout(win, delay);
680     getyx(win, first_y, first_x);
681
682     wgetch_help(win, flags);
683     wsetscrreg(win, first_y, getmaxy(win) - 1);
684     scrollok(win, TRUE);
685
686     for (;;) {
687         while ((c = wGetchar(win)) == ERR) {
688             incount++;
689             if (blocking) {
690                 (void) wprintw(win, "%05d: input error", incount);
691                 break;
692             } else {
693                 (void) wprintw(win, "%05d: input timed out", incount);
694             }
695             wgetch_wrap(win, first_y);
696         }
697         if (c == ERR && blocking) {
698             wprintw(win, "ERR");
699             wgetch_wrap(win, first_y);
700         } else if (isQuit(c)) {
701             break;
702         } else if (c == 'e') {
703             flags[UChar('e')] = !flags[UChar('e')];
704             setup_getch(win, flags);
705             wgetch_help(win, flags);
706         } else if (c == 'g') {
707             waddstr(win, "getstr test: ");
708             echo();
709             wgetnstr(win, buf, sizeof(buf) - 1);
710             noecho();
711             wprintw(win, "I saw %d characters:\n\t`%s'.", (int) strlen(buf), buf);
712             wclrtoeol(win);
713             wgetch_wrap(win, first_y);
714         } else if (c == 'k') {
715             flags[UChar('k')] = !flags[UChar('k')];
716             setup_getch(win, flags);
717             wgetch_help(win, flags);
718         } else if (c == 'm') {
719             flags[UChar('m')] = !flags[UChar('m')];
720             setup_getch(win, flags);
721             wgetch_help(win, flags);
722         } else if (c == 's') {
723             ShellOut(TRUE);
724         } else if (c == 'w') {
725             int high = getmaxy(win) - 1 - first_y + 1;
726             int wide = getmaxx(win) - first_x;
727             int old_y, old_x;
728             int new_y = first_y + getbegy(win);
729             int new_x = first_x + getbegx(win);
730
731             getyx(win, old_y, old_x);
732             if (high > 2 && wide > 2) {
733                 WINDOW *wb = newwin(high, wide, new_y, new_x);
734                 WINDOW *wi = newwin(high - 2, wide - 2, new_y + 1, new_x + 1);
735
736                 box(wb, 0, 0);
737                 wrefresh(wb);
738                 wmove(wi, 0, 0);
739                 remember_boxes(level, wi, wb);
740                 wgetch_test(level + 1, wi, delay);
741                 delwin(wi);
742                 delwin(wb);
743
744                 wgetch_help(win, flags);
745                 wmove(win, old_y, old_x);
746                 touchwin(win);
747                 wrefresh(win);
748                 doupdate();
749             }
750 #ifdef SIGTSTP
751         } else if (c == 'z') {
752             kill(getpid(), SIGTSTP);
753 #endif
754         } else {
755             wprintw(win, "Key pressed: %04o ", c);
756 #ifdef NCURSES_MOUSE_VERSION
757             if (c == KEY_MOUSE) {
758                 int y, x;
759                 MEVENT event;
760
761                 getmouse(&event);
762                 wprintw(win, "KEY_MOUSE, %s", mouse_decode(&event));
763                 getyx(win, y, x);
764                 move(event.y, event.x);
765                 addch('*');
766                 wmove(win, y, x);
767             } else
768 #endif /* NCURSES_MOUSE_VERSION */
769             if (c >= KEY_MIN) {
770 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
771                 if (c == KEY_RESIZE) {
772                     resize_boxes(level, win);
773                 }
774 #endif
775                 (void) waddstr(win, keyname(c));
776             } else if (c > 0x80) {
777                 unsigned c2 = (c & 0x7f);
778                 if (isprint(c2))
779                     (void) wprintw(win, "M-%c", UChar(c2));
780                 else
781                     (void) wprintw(win, "M-%s", unctrl(c2));
782                 waddstr(win, " (high-half character)");
783             } else {
784                 if (isprint(c))
785                     (void) wprintw(win, "%c (ASCII printable character)", c);
786                 else
787                     (void) wprintw(win, "%s (ASCII control character)",
788                                    unctrl(UChar(c)));
789             }
790             wgetch_wrap(win, first_y);
791         }
792     }
793
794     wtimeout(win, -1);
795 }
796
797 static int
798 begin_getch_test(void)
799 {
800     char buf[BUFSIZ];
801     int delay;
802
803     refresh();
804
805 #ifdef NCURSES_MOUSE_VERSION
806     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
807 #endif
808
809     (void) printw("Delay in 10ths of a second (<CR> for blocking input)? ");
810     echo();
811     getnstr(buf, sizeof(buf) - 1);
812     noecho();
813     nonl();
814
815     if (isdigit(UChar(buf[0]))) {
816         delay = atoi(buf) * 100;
817     } else {
818         delay = -1;
819     }
820     raw();
821     move(5, 0);
822     return delay;
823 }
824
825 static void
826 finish_getch_test(void)
827 {
828 #ifdef NCURSES_MOUSE_VERSION
829     mousemask(0, (mmask_t *) 0);
830 #endif
831     erase();
832     noraw();
833     nl();
834     endwin();
835 }
836
837 static void
838 getch_test(void)
839 {
840     int delay = begin_getch_test();
841
842     slk_restore();
843     wgetch_test(0, stdscr, delay);
844     forget_boxes();
845     finish_getch_test();
846 }
847
848 #if USE_WIDEC_SUPPORT
849 /*
850  * For wget_wch_test(), we create pairs of windows - one for a box, one for text.
851  * Resize both and paint the box in the parent.
852  */
853 #ifdef KEY_RESIZE
854 static void
855 resize_wide_boxes(unsigned level, WINDOW *win)
856 {
857     unsigned n;
858     int base = 5;
859     int high = LINES - base;
860     int wide = COLS;
861
862     touchwin(stdscr);
863     wnoutrefresh(stdscr);
864
865     slk_repaint();
866
867     for (n = 0; n < level; ++n) {
868         wresize(winstack[n].frame, high, wide);
869         wresize(winstack[n].text, high - 2, wide - 2);
870         high -= 2;
871         wide -= 2;
872         werase(winstack[n].text);
873         box_set(winstack[n].frame, 0, 0);
874         wnoutrefresh(winstack[n].frame);
875         wprintw(winstack[n].text,
876                 "size %dx%d\n",
877                 getmaxy(winstack[n].text),
878                 getmaxx(winstack[n].text));
879         wnoutrefresh(winstack[n].text);
880         if (winstack[n].text == win)
881             break;
882     }
883     doupdate();
884 }
885 #endif /* KEY_RESIZE */
886
887 static char *
888 wcstos(const wchar_t *src)
889 {
890     int need;
891     mbstate_t state;
892     char *result = 0;
893     const wchar_t *tmp = src;
894
895     memset(&state, 0, sizeof(state));
896     if ((need = wcsrtombs(0, &tmp, 0, &state)) > 0) {
897         unsigned have = need;
898         result = (char *) calloc(have + 1, 1);
899         tmp = src;
900         if (wcsrtombs(result, &tmp, have, &state) != have) {
901             free(result);
902             result = 0;
903         }
904     }
905     return result;
906 }
907
908 static void
909 wget_wch_test(unsigned level, WINDOW *win, int delay)
910 {
911     wchar_t wchar_buf[BUFSIZ];
912     wint_t wint_buf[BUFSIZ];
913     int first_y, first_x;
914     wint_t c;
915     int incount = 0;
916     bool flags[256];
917     bool blocking = (delay < 0);
918     int y, x, code;
919     char *temp;
920
921     memset(flags, FALSE, sizeof(flags));
922     flags[UChar('k')] = (win == stdscr);
923
924     setup_getch(win, flags);
925     wtimeout(win, delay);
926     getyx(win, first_y, first_x);
927
928     wgetch_help(win, flags);
929     wsetscrreg(win, first_y, getmaxy(win) - 1);
930     scrollok(win, TRUE);
931
932     for (;;) {
933         while ((code = wGet_wchar(win, &c)) == ERR) {
934             incount++;
935             if (blocking) {
936                 (void) wprintw(win, "%05d: input error", incount);
937                 break;
938             } else {
939                 (void) wprintw(win, "%05d: input timed out", incount);
940             }
941             wgetch_wrap(win, first_y);
942         }
943         if (code == ERR && blocking) {
944             wprintw(win, "ERR");
945             wgetch_wrap(win, first_y);
946         } else if (isQuit((int) c)) {
947             break;
948         } else if (c == 'e') {
949             flags[UChar('e')] = !flags[UChar('e')];
950             setup_getch(win, flags);
951             wgetch_help(win, flags);
952         } else if (c == 'g') {
953             waddstr(win, "getstr test: ");
954             echo();
955             code = wgetn_wstr(win, wint_buf, sizeof(wint_buf) - 1);
956             noecho();
957             if (code == ERR) {
958                 wprintw(win, "wgetn_wstr returns an error.");
959             } else {
960                 int n;
961                 for (n = 0; (wchar_buf[n] = wint_buf[n]) != 0; ++n) ;
962                 if ((temp = wcstos(wchar_buf)) != 0) {
963                     wprintw(win, "I saw %d characters:\n\t`%s'.",
964                             (int) wcslen(wchar_buf), temp);
965                     free(temp);
966                 } else {
967                     wprintw(win, "I saw %d characters (cannot convert).",
968                             (int) wcslen(wchar_buf));
969                 }
970             }
971             wclrtoeol(win);
972             wgetch_wrap(win, first_y);
973         } else if (c == 'k') {
974             flags[UChar('k')] = !flags[UChar('k')];
975             setup_getch(win, flags);
976             wgetch_help(win, flags);
977         } else if (c == 'm') {
978             flags[UChar('m')] = !flags[UChar('m')];
979             setup_getch(win, flags);
980             wgetch_help(win, flags);
981         } else if (c == 's') {
982             ShellOut(TRUE);
983         } else if (c == 'w') {
984             int high = getmaxy(win) - 1 - first_y + 1;
985             int wide = getmaxx(win) - first_x;
986             int old_y, old_x;
987             int new_y = first_y + getbegy(win);
988             int new_x = first_x + getbegx(win);
989
990             getyx(win, old_y, old_x);
991             if (high > 2 && wide > 2) {
992                 WINDOW *wb = newwin(high, wide, new_y, new_x);
993                 WINDOW *wi = newwin(high - 2, wide - 2, new_y + 1, new_x + 1);
994
995                 box_set(wb, 0, 0);
996                 wrefresh(wb);
997                 wmove(wi, 0, 0);
998                 remember_boxes(level, wi, wb);
999                 wget_wch_test(level + 1, wi, delay);
1000                 delwin(wi);
1001                 delwin(wb);
1002
1003                 wgetch_help(win, flags);
1004                 wmove(win, old_y, old_x);
1005                 touchwin(win);
1006                 wrefresh(win);
1007             }
1008 #ifdef SIGTSTP
1009         } else if (c == 'z') {
1010             kill(getpid(), SIGTSTP);
1011 #endif
1012         } else {
1013             wprintw(win, "Key pressed: %04o ", c);
1014 #ifdef NCURSES_MOUSE_VERSION
1015             if (c == KEY_MOUSE) {
1016                 MEVENT event;
1017
1018                 getmouse(&event);
1019                 wprintw(win, "KEY_MOUSE, %s", mouse_decode(&event));
1020                 getyx(win, y, x);
1021                 move(event.y, event.x);
1022                 addch('*');
1023                 wmove(win, y, x);
1024             } else
1025 #endif /* NCURSES_MOUSE_VERSION */
1026             if (code == KEY_CODE_YES) {
1027 #ifdef KEY_RESIZE
1028                 if (c == KEY_RESIZE) {
1029                     resize_wide_boxes(level, win);
1030                 }
1031 #endif
1032                 (void) waddstr(win, key_name((wchar_t) c));
1033             } else {
1034                 if (c < 256 && iscntrl(c)) {
1035                     (void) wprintw(win, "%s (control character)", unctrl(c));
1036                 } else {
1037                     wchar_t c2 = c;
1038                     waddnwstr(win, &c2, 1);
1039                     (void) wprintw(win, " = %#x (printable character)", c);
1040                 }
1041             }
1042             wgetch_wrap(win, first_y);
1043         }
1044     }
1045
1046     wtimeout(win, -1);
1047 }
1048
1049 static void
1050 get_wch_test(void)
1051 {
1052     int delay = begin_getch_test();
1053
1054     slk_restore();
1055     wget_wch_test(0, stdscr, delay);
1056     forget_boxes();
1057     finish_getch_test();
1058 }
1059 #endif
1060
1061 /****************************************************************************
1062  *
1063  * Character attributes test
1064  *
1065  ****************************************************************************/
1066
1067 #if HAVE_SETUPTERM || HAVE_TGETENT
1068 #define get_ncv() TIGETNUM("ncv","NC")
1069 #define get_xmc() TIGETNUM("xmc","sg")
1070 #else
1071 #define get_ncv() -1
1072 #define get_xmc() -1
1073 #endif
1074
1075 #if !HAVE_TERMATTRS
1076 static chtype
1077 my_termattrs(void)
1078 {
1079     static int first = TRUE;
1080     static chtype result = 0;
1081
1082     if (first) {
1083 #if !HAVE_TIGETSTR
1084         char buffer[4096];
1085         char parsed[4096];
1086         char *area_pointer = parsed;
1087
1088         tgetent(buffer, getenv("TERM"));
1089 #endif
1090
1091         if (TIGETSTR("smso", "so"))
1092             result |= A_STANDOUT;
1093         if (TIGETSTR("smul", "us"))
1094             result |= A_UNDERLINE;
1095         if (TIGETSTR("rev", "mr"))
1096             result |= A_REVERSE;
1097         if (TIGETSTR("blink", "mb"))
1098             result |= A_BLINK;
1099         if (TIGETSTR("dim", "mh"))
1100             result |= A_DIM;
1101         if (TIGETSTR("bold", "md"))
1102             result |= A_BOLD;
1103         if (TIGETSTR("smacs", "ac"))
1104             result |= A_ALTCHARSET;
1105
1106         first = FALSE;
1107     }
1108     return result;
1109 }
1110 #define termattrs() my_termattrs()
1111 #endif
1112
1113 #define MAX_ATTRSTRING 31
1114 #define LEN_ATTRSTRING 26
1115
1116 static char attr_test_string[MAX_ATTRSTRING + 1];
1117
1118 static void
1119 attr_legend(WINDOW *helpwin)
1120 {
1121     int row = 1;
1122     int col = 1;
1123
1124     mvwprintw(helpwin, row++, col,
1125               "ESC to exit.");
1126     mvwprintw(helpwin, row++, col,
1127               "^L repaints.");
1128     ++row;
1129     mvwprintw(helpwin, row++, col,
1130               "Modify the test strings:");
1131     mvwprintw(helpwin, row++, col,
1132               "  A digit sets gaps on each side of displayed attributes");
1133     mvwprintw(helpwin, row++, col,
1134               "  </> shifts the text left/right. ");
1135     ++row;
1136     mvwprintw(helpwin, row++, col,
1137               "Toggles:");
1138     if (use_colors) {
1139         mvwprintw(helpwin, row++, col,
1140                   "  f/F/b/F toggle foreground/background background color");
1141         mvwprintw(helpwin, row++, col,
1142                   "  t/T     toggle text/background color attribute");
1143     }
1144     mvwprintw(helpwin, row++, col,
1145               "  a/A     toggle ACS (alternate character set) mapping");
1146     mvwprintw(helpwin, row++, col,
1147               "  v/V     toggle video attribute to combine with each line");
1148 }
1149
1150 static void
1151 show_color_attr(int fg, int bg, int tx)
1152 {
1153     if (use_colors) {
1154         printw("  Colors (fg %d, bg %d", fg, bg);
1155         if (tx >= 0)
1156             printw(", text %d", tx);
1157         printw("),");
1158     }
1159 }
1160
1161 static bool
1162 cycle_color_attr(int ch, short *fg, short *bg, short *tx)
1163 {
1164     bool error = FALSE;
1165
1166     if (use_colors) {
1167         switch (ch) {
1168         case 'f':
1169             *fg = (*fg + 1);
1170             break;
1171         case 'F':
1172             *fg = (*fg - 1);
1173             break;
1174         case 'b':
1175             *bg = (*bg + 1);
1176             break;
1177         case 'B':
1178             *bg = (*bg - 1);
1179             break;
1180         case 't':
1181             *tx = (*tx + 1);
1182             break;
1183         case 'T':
1184             *tx = (*tx - 1);
1185             break;
1186         default:
1187             beep();
1188             error = TRUE;
1189             break;
1190         }
1191         if (*fg >= COLORS)
1192             *fg = min_colors;
1193         if (*fg < min_colors)
1194             *fg = COLORS - 1;
1195         if (*bg >= COLORS)
1196             *bg = min_colors;
1197         if (*bg < min_colors)
1198             *bg = COLORS - 1;
1199         if (*tx >= COLORS)
1200             *tx = -1;
1201         if (*tx < -1)
1202             *tx = COLORS - 1;
1203     } else {
1204         beep();
1205         error = TRUE;
1206     }
1207     return error;
1208 }
1209
1210 static void
1211 adjust_attr_string(int adjust)
1212 {
1213     int first = ((int) UChar(attr_test_string[0])) + adjust;
1214     int last = first + LEN_ATTRSTRING;
1215
1216     if (first >= ' ' && last <= '~') {  /* 32..126 */
1217         int j, k;
1218         for (j = 0, k = first; j < MAX_ATTRSTRING && k <= last; ++j, ++k) {
1219             attr_test_string[j] = k;
1220             if (((k + 1 - first) % 5) == 0) {
1221                 ++j;
1222                 if (j < MAX_ATTRSTRING)
1223                     attr_test_string[j] = ' ';
1224             }
1225         }
1226         while (j < MAX_ATTRSTRING)
1227             attr_test_string[j++] = ' ';
1228         attr_test_string[j] = '\0';
1229     } else {
1230         beep();
1231     }
1232 }
1233
1234 static void
1235 init_attr_string(void)
1236 {
1237     attr_test_string[0] = 'a';
1238     adjust_attr_string(0);
1239 }
1240
1241 static int
1242 show_attr(int row, int skip, bool arrow, chtype attr, const char *name)
1243 {
1244     int ncv = get_ncv();
1245     chtype test = attr & (chtype) (~A_ALTCHARSET);
1246
1247     if (arrow)
1248         mvprintw(row, 5, "-->");
1249     mvprintw(row, 8, "%s mode:", name);
1250     mvprintw(row, 24, "|");
1251     if (skip)
1252         printw("%*s", skip, " ");
1253     /*
1254      * Just for testing, write text using the alternate character set one
1255      * character at a time (to pass its rendition directly), and use the
1256      * string operation for the other attributes.
1257      */
1258     if (attr & A_ALTCHARSET) {
1259         const char *s;
1260         chtype ch;
1261
1262         for (s = attr_test_string; *s != '\0'; ++s) {
1263             ch = UChar(*s);
1264             addch(ch | attr);
1265         }
1266     } else {
1267         attrset(attr);
1268         addstr(attr_test_string);
1269         attroff(attr);
1270     }
1271     if (skip)
1272         printw("%*s", skip, " ");
1273     printw("|");
1274     if (test != A_NORMAL) {
1275         if (!(termattrs() & test)) {
1276             printw(" (N/A)");
1277         } else {
1278             if (ncv > 0 && (getbkgd(stdscr) & A_COLOR)) {
1279                 static const chtype table[] =
1280                 {
1281                     A_STANDOUT,
1282                     A_UNDERLINE,
1283                     A_REVERSE,
1284                     A_BLINK,
1285                     A_DIM,
1286                     A_BOLD,
1287 #ifdef A_INVIS
1288                     A_INVIS,
1289 #endif
1290                     A_PROTECT,
1291                     A_ALTCHARSET
1292                 };
1293                 unsigned n;
1294                 bool found = FALSE;
1295                 for (n = 0; n < SIZEOF(table); n++) {
1296                     if ((table[n] & attr) != 0
1297                         && ((1 << n) & ncv) != 0) {
1298                         found = TRUE;
1299                         break;
1300                     }
1301                 }
1302                 if (found)
1303                     printw(" (NCV)");
1304             }
1305             if ((termattrs() & test) != test)
1306                 printw(" (Part)");
1307         }
1308     }
1309     return row + 2;
1310 }
1311 /* *INDENT-OFF* */
1312 static const struct {
1313     attr_t                      attr;
1314     NCURSES_CONST char *        name;
1315 } attrs_to_test[] = {
1316     { A_STANDOUT,       "STANDOUT" },
1317     { A_REVERSE,        "REVERSE" },
1318     { A_BOLD,           "BOLD" },
1319     { A_UNDERLINE,      "UNDERLINE" },
1320     { A_DIM,            "DIM" },
1321     { A_BLINK,          "BLINK" },
1322     { A_PROTECT,        "PROTECT" },
1323 #ifdef A_INVIS
1324     { A_INVIS,          "INVISIBLE" },
1325 #endif
1326     { A_NORMAL,         "NORMAL" },
1327 };
1328 /* *INDENT-ON* */
1329
1330 static bool
1331 attr_getc(int *skip, short *fg, short *bg, short *tx, int *ac, unsigned *kc)
1332 {
1333     bool result = TRUE;
1334     bool error = FALSE;
1335     WINDOW *helpwin;
1336
1337     do {
1338         int ch = Getchar();
1339
1340         error = FALSE;
1341         if (ch < 256 && isdigit(ch)) {
1342             *skip = (ch - '0');
1343         } else {
1344             switch (ch) {
1345             case CTRL('L'):
1346                 Repaint();
1347                 break;
1348             case '?':
1349                 if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
1350                     box(helpwin, 0, 0);
1351                     attr_legend(helpwin);
1352                     wGetchar(helpwin);
1353                     delwin(helpwin);
1354                 }
1355                 break;
1356             case 'a':
1357                 *ac = 0;
1358                 break;
1359             case 'A':
1360                 *ac = A_ALTCHARSET;
1361                 break;
1362             case 'v':
1363                 if (*kc == 0)
1364                     *kc = SIZEOF(attrs_to_test) - 1;
1365                 else
1366                     *kc -= 1;
1367                 break;
1368             case 'V':
1369                 *kc += 1;
1370                 if (*kc >= SIZEOF(attrs_to_test))
1371                     *kc = 0;
1372                 break;
1373             case '<':
1374                 adjust_attr_string(-1);
1375                 break;
1376             case '>':
1377                 adjust_attr_string(1);
1378                 break;
1379               case_QUIT:
1380                 result = FALSE;
1381                 break;
1382             default:
1383                 error = cycle_color_attr(ch, fg, bg, tx);
1384                 break;
1385             }
1386         }
1387     } while (error);
1388     return result;
1389 }
1390
1391 static void
1392 attr_test(void)
1393 /* test text attributes */
1394 {
1395     int n;
1396     int skip = get_xmc();
1397     short fg = COLOR_BLACK;     /* color pair 0 is special */
1398     short bg = COLOR_BLACK;
1399     short tx = -1;
1400     int ac = 0;
1401     unsigned j, k;
1402
1403     if (skip < 0)
1404         skip = 0;
1405
1406     n = skip;                   /* make it easy */
1407     k = SIZEOF(attrs_to_test) - 1;
1408     init_attr_string();
1409
1410     do {
1411         int row = 2;
1412         chtype normal = A_NORMAL | BLANK;
1413         chtype extras = ac;
1414
1415         if (use_colors) {
1416             short pair = (fg != COLOR_BLACK || bg != COLOR_BLACK);
1417             if (pair != 0) {
1418                 pair = 1;
1419                 if (init_pair(pair, fg, bg) == ERR) {
1420                     beep();
1421                 } else {
1422                     normal |= COLOR_PAIR(pair);
1423                 }
1424             }
1425             if (tx >= 0) {
1426                 pair = 2;
1427                 if (init_pair(pair, tx, bg) == ERR) {
1428                     beep();
1429                 } else {
1430                     extras |= COLOR_PAIR(pair);
1431                 }
1432             }
1433         }
1434         bkgd(normal);
1435         bkgdset(normal);
1436         erase();
1437
1438         box(stdscr, 0, 0);
1439         mvaddstr(0, 20, "Character attribute test display");
1440
1441         for (j = 0; j < SIZEOF(attrs_to_test); ++j) {
1442             bool arrow = (j == k);
1443             row = show_attr(row, n, arrow,
1444                             extras |
1445                             attrs_to_test[j].attr |
1446                             attrs_to_test[k].attr,
1447                             attrs_to_test[j].name);
1448         }
1449
1450         mvprintw(row, 8,
1451                  "This terminal does %shave the magic-cookie glitch",
1452                  get_xmc() > -1 ? "" : "not ");
1453         mvprintw(row + 1, 8, "Enter '?' for help.");
1454         show_color_attr(fg, bg, tx);
1455         printw("  ACS (%d)", ac != 0);
1456
1457         refresh();
1458     } while (attr_getc(&n, &fg, &bg, &tx, &ac, &k));
1459
1460     bkgdset(A_NORMAL | BLANK);
1461     erase();
1462     endwin();
1463 }
1464
1465 #if USE_WIDEC_SUPPORT
1466 static wchar_t wide_attr_test_string[MAX_ATTRSTRING + 1];
1467
1468 static void
1469 wide_adjust_attr_string(int adjust)
1470 {
1471     int first = ((int) UChar(wide_attr_test_string[0])) + adjust;
1472     int last = first + LEN_ATTRSTRING;
1473
1474     if (first >= ' ' && last <= '~') {  /* 32..126 */
1475         int j, k;
1476         for (j = 0, k = first; j < MAX_ATTRSTRING && k <= last; ++j, ++k) {
1477             wide_attr_test_string[j] = k;
1478             if (((k + 1 - first) % 5) == 0) {
1479                 ++j;
1480                 if (j < MAX_ATTRSTRING)
1481                     wide_attr_test_string[j] = ' ';
1482             }
1483         }
1484         while (j < MAX_ATTRSTRING)
1485             wide_attr_test_string[j++] = ' ';
1486         wide_attr_test_string[j] = '\0';
1487     } else {
1488         beep();
1489     }
1490 }
1491
1492 static void
1493 wide_init_attr_string(void)
1494 {
1495     wide_attr_test_string[0] = 'a';
1496     wide_adjust_attr_string(0);
1497 }
1498
1499 static void
1500 set_wide_background(short pair)
1501 {
1502     cchar_t normal;
1503     wchar_t blank[2];
1504
1505     blank[0] = ' ';
1506     blank[1] = 0;
1507     setcchar(&normal, blank, A_NORMAL, pair, 0);
1508     bkgrnd(&normal);
1509     bkgrndset(&normal);
1510 }
1511
1512 static attr_t
1513 get_wide_background(void)
1514 {
1515     attr_t result = A_NORMAL;
1516     attr_t attr;
1517     cchar_t ch;
1518     short pair;
1519     wchar_t wch[10];
1520
1521     if (getbkgrnd(&ch) != ERR) {
1522         if (getcchar(&ch, wch, &attr, &pair, 0) != ERR) {
1523             result = attr;
1524         }
1525     }
1526     return result;
1527 }
1528
1529 static int
1530 wide_show_attr(int row, int skip, bool arrow, chtype attr, short pair, const char *name)
1531 {
1532     int ncv = get_ncv();
1533     chtype test = attr & ~WA_ALTCHARSET;
1534
1535     if (arrow)
1536         mvprintw(row, 5, "-->");
1537     mvprintw(row, 8, "%s mode:", name);
1538     mvprintw(row, 24, "|");
1539     if (skip)
1540         printw("%*s", skip, " ");
1541
1542     /*
1543      * Just for testing, write text using the alternate character set one
1544      * character at a time (to pass its rendition directly), and use the
1545      * string operation for the other attributes.
1546      */
1547     if (attr & WA_ALTCHARSET) {
1548         const wchar_t *s;
1549         cchar_t ch;
1550
1551         for (s = wide_attr_test_string; *s != L'\0'; ++s) {
1552             wchar_t fill[2];
1553             fill[0] = *s;
1554             fill[1] = L'\0';
1555             setcchar(&ch, fill, attr, pair, 0);
1556             add_wch(&ch);
1557         }
1558     } else {
1559         attr_t old_attr;
1560         short old_pair;
1561
1562         attr_get(&old_attr, &old_pair, 0);
1563         attr_set(attr, pair, 0);
1564         addwstr(wide_attr_test_string);
1565         attr_set(old_attr, old_pair, 0);
1566     }
1567     if (skip)
1568         printw("%*s", skip, " ");
1569     printw("|");
1570     if (test != A_NORMAL) {
1571         if (!(term_attrs() & test)) {
1572             printw(" (N/A)");
1573         } else {
1574             if (ncv > 0 && (get_wide_background() & A_COLOR)) {
1575                 static const attr_t table[] =
1576                 {
1577                     WA_STANDOUT,
1578                     WA_UNDERLINE,
1579                     WA_REVERSE,
1580                     WA_BLINK,
1581                     WA_DIM,
1582                     WA_BOLD,
1583                     WA_INVIS,
1584                     WA_PROTECT,
1585                     WA_ALTCHARSET
1586                 };
1587                 unsigned n;
1588                 bool found = FALSE;
1589                 for (n = 0; n < SIZEOF(table); n++) {
1590                     if ((table[n] & attr) != 0
1591                         && ((1 << n) & ncv) != 0) {
1592                         found = TRUE;
1593                         break;
1594                     }
1595                 }
1596                 if (found)
1597                     printw(" (NCV)");
1598             }
1599             if ((term_attrs() & test) != test)
1600                 printw(" (Part)");
1601         }
1602     }
1603     return row + 2;
1604 }
1605
1606 static bool
1607 wide_attr_getc(int *skip, short *fg, short *bg, short *tx, int *ac, unsigned *kc)
1608 {
1609     bool result = TRUE;
1610     bool error = FALSE;
1611     WINDOW *helpwin;
1612
1613     do {
1614         int ch = Getchar();
1615
1616         error = FALSE;
1617         if (ch < 256 && isdigit(ch)) {
1618             *skip = (ch - '0');
1619         } else {
1620             switch (ch) {
1621             case CTRL('L'):
1622                 Repaint();
1623                 break;
1624             case '?':
1625                 if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
1626                     box_set(helpwin, 0, 0);
1627                     attr_legend(helpwin);
1628                     wGetchar(helpwin);
1629                     delwin(helpwin);
1630                 }
1631                 break;
1632             case 'a':
1633                 *ac = 0;
1634                 break;
1635             case 'A':
1636                 *ac = A_ALTCHARSET;
1637                 break;
1638             case 'v':
1639                 if (*kc == 0)
1640                     *kc = SIZEOF(attrs_to_test) - 1;
1641                 else
1642                     *kc -= 1;
1643                 break;
1644             case 'V':
1645                 *kc += 1;
1646                 if (*kc >= SIZEOF(attrs_to_test))
1647                     *kc = 0;
1648                 break;
1649             case '<':
1650                 wide_adjust_attr_string(-1);
1651                 break;
1652             case '>':
1653                 wide_adjust_attr_string(1);
1654                 break;
1655               case_QUIT:
1656                 result = FALSE;
1657                 break;
1658             default:
1659                 error = cycle_color_attr(ch, fg, bg, tx);
1660                 break;
1661             }
1662         }
1663     } while (error);
1664     return result;
1665 }
1666
1667 static void
1668 wide_attr_test(void)
1669 /* test text attributes using wide-character calls */
1670 {
1671     int n;
1672     int skip = get_xmc();
1673     short fg = COLOR_BLACK;     /* color pair 0 is special */
1674     short bg = COLOR_BLACK;
1675     short tx = -1;
1676     int ac = 0;
1677     unsigned j, k;
1678
1679     if (skip < 0)
1680         skip = 0;
1681
1682     n = skip;                   /* make it easy */
1683     k = SIZEOF(attrs_to_test) - 1;
1684     wide_init_attr_string();
1685
1686     do {
1687         int row = 2;
1688         short pair = 0;
1689         short extras = 0;
1690
1691         if (use_colors) {
1692             pair = (fg != COLOR_BLACK || bg != COLOR_BLACK);
1693             if (pair != 0) {
1694                 pair = 1;
1695                 if (init_pair(pair, fg, bg) == ERR) {
1696                     beep();
1697                 }
1698             }
1699             extras = pair;
1700             if (tx >= 0) {
1701                 extras = 2;
1702                 if (init_pair(extras, tx, bg) == ERR) {
1703                     beep();
1704                 }
1705             }
1706         }
1707         set_wide_background(pair);
1708         erase();
1709
1710         box_set(stdscr, 0, 0);
1711         mvaddstr(0, 20, "Character attribute test display");
1712
1713         for (j = 0; j < SIZEOF(attrs_to_test); ++j) {
1714             row = wide_show_attr(row, n, j == k,
1715                                  ac |
1716                                  attrs_to_test[j].attr |
1717                                  attrs_to_test[k].attr,
1718                                  extras,
1719                                  attrs_to_test[j].name);
1720         }
1721
1722         mvprintw(row, 8,
1723                  "This terminal does %shave the magic-cookie glitch",
1724                  get_xmc() > -1 ? "" : "not ");
1725         mvprintw(row + 1, 8, "Enter '?' for help.");
1726         show_color_attr(fg, bg, tx);
1727         printw("  ACS (%d)", ac != 0);
1728
1729         refresh();
1730     } while (wide_attr_getc(&n, &fg, &bg, &tx, &ac, &k));
1731
1732     set_wide_background(0);
1733     erase();
1734     endwin();
1735 }
1736 #endif
1737
1738 /****************************************************************************
1739  *
1740  * Color support tests
1741  *
1742  ****************************************************************************/
1743
1744 static NCURSES_CONST char *the_color_names[] =
1745 {
1746     "black",
1747     "red",
1748     "green",
1749     "yellow",
1750     "blue",
1751     "magenta",
1752     "cyan",
1753     "white",
1754     "BLACK",
1755     "RED",
1756     "GREEN",
1757     "YELLOW",
1758     "BLUE",
1759     "MAGENTA",
1760     "CYAN",
1761     "WHITE"
1762 };
1763
1764 static void
1765 show_color_name(int y, int x, int color, bool wide)
1766 {
1767     if (move(y, x) != ERR) {
1768         char temp[80];
1769         int width = 8;
1770
1771         if (wide) {
1772             sprintf(temp, "%02d", color);
1773             width = 4;
1774         } else if (color >= 8) {
1775             sprintf(temp, "[%02d]", color);
1776         } else {
1777             strcpy(temp, the_color_names[color]);
1778         }
1779         printw("%-*.*s", width, width, temp);
1780     }
1781 }
1782
1783 static void
1784 color_legend(WINDOW *helpwin)
1785 {
1786     int row = 1;
1787     int col = 1;
1788
1789     mvwprintw(helpwin, row++, col,
1790               "ESC to exit.");
1791     ++row;
1792     mvwprintw(helpwin, row++, col,
1793               "Use up/down arrow to scroll through the display if it is");
1794     mvwprintw(helpwin, row++, col,
1795               "longer than one screen. Control/N and Control/P can be used");
1796     mvwprintw(helpwin, row++, col,
1797               "in place up up/down arrow.  Use pageup/pagedown to scroll a");
1798     mvwprintw(helpwin, row++, col,
1799               "full screen; control/B and control/F can be used here.");
1800     ++row;
1801     mvwprintw(helpwin, row++, col,
1802               "Toggles:");
1803     mvwprintw(helpwin, row++, col,
1804               "  b/B     toggle bold off/on");
1805     mvwprintw(helpwin, row++, col,
1806               "  n/N     toggle text/number on/off");
1807     mvwprintw(helpwin, row++, col,
1808               "  w/W     toggle width between 8/16 colors");
1809 }
1810
1811 #define set_color_test(name, value) if (name != value) { name = value; base_row = 0; }
1812
1813 /* generate a color test pattern */
1814 static void
1815 color_test(void)
1816 {
1817     short i;
1818     int top = 0, width;
1819     int base_row = 0;
1820     int grid_top = top + 3;
1821     int page_size = (LINES - grid_top);
1822     int pairs_max = PAIR_NUMBER(A_COLOR) + 1;
1823     int row_limit;
1824     int per_row;
1825     char numbered[80];
1826     const char *hello;
1827     bool done = FALSE;
1828     bool opt_bold = FALSE;
1829     bool opt_wide = FALSE;
1830     bool opt_nums = FALSE;
1831     WINDOW *helpwin;
1832
1833     if (pairs_max > COLOR_PAIRS)
1834         pairs_max = COLOR_PAIRS;
1835
1836     while (!done) {
1837         int shown = 0;
1838
1839         /* this assumes an 80-column line */
1840         if (opt_wide) {
1841             width = 4;
1842             hello = "Test";
1843             per_row = (COLORS > 8) ? 16 : 8;
1844         } else {
1845             width = 8;
1846             hello = "Hello";
1847             per_row = 8;
1848         }
1849
1850         row_limit = (pairs_max + per_row - 1) / per_row;
1851
1852         move(0, 0);
1853         (void) printw("There are %d color pairs and %d colors\n",
1854                       pairs_max, COLORS);
1855
1856         clrtobot();
1857         (void) mvprintw(top + 1, 0,
1858                         "%dx%d matrix of foreground/background colors, bold *%s*\n",
1859                         row_limit,
1860                         per_row,
1861                         opt_bold ? "on" : "off");
1862
1863         /* show color names/numbers across the top */
1864         for (i = 0; i < per_row; i++)
1865             show_color_name(top + 2, (i + 1) * width, i, opt_wide);
1866
1867         /* show a grid of colors, with color names/ numbers on the left */
1868         for (i = (base_row * per_row); i < pairs_max; i++) {
1869             int row = grid_top + (i / per_row) - base_row;
1870             int col = (i % per_row + 1) * width;
1871             short pair = i;
1872
1873             if (row >= 0 && move(row, col) != ERR) {
1874                 short fg = i % COLORS;
1875                 short bg = i / COLORS;
1876
1877                 init_pair(pair, fg, bg);
1878                 attron((attr_t) COLOR_PAIR(pair));
1879                 if (opt_bold)
1880                     attron((attr_t) A_BOLD);
1881
1882                 if (opt_nums) {
1883                     sprintf(numbered, "{%02X}", i);
1884                     hello = numbered;
1885                 }
1886                 printw("%-*.*s", width, width, hello);
1887                 attrset(A_NORMAL);
1888
1889                 if ((i % per_row) == 0 && (i % COLORS) == 0) {
1890                     show_color_name(row, 0, i / COLORS, opt_wide);
1891                 }
1892                 ++shown;
1893             } else if (shown) {
1894                 break;
1895             }
1896         }
1897
1898         switch (wGetchar(stdscr)) {
1899         case 'b':
1900             opt_bold = FALSE;
1901             break;
1902         case 'B':
1903             opt_bold = TRUE;
1904             break;
1905         case 'n':
1906             opt_nums = FALSE;
1907             break;
1908         case 'N':
1909             opt_nums = TRUE;
1910             break;
1911           case_QUIT:
1912             done = TRUE;
1913             continue;
1914         case 'w':
1915             set_color_test(opt_wide, FALSE);
1916             break;
1917         case 'W':
1918             set_color_test(opt_wide, TRUE);
1919             break;
1920         case CTRL('p'):
1921         case KEY_UP:
1922             if (base_row <= 0) {
1923                 beep();
1924             } else {
1925                 base_row -= 1;
1926             }
1927             break;
1928         case CTRL('n'):
1929         case KEY_DOWN:
1930             if (base_row + page_size >= row_limit) {
1931                 beep();
1932             } else {
1933                 base_row += 1;
1934             }
1935             break;
1936         case CTRL('b'):
1937         case KEY_PREVIOUS:
1938         case KEY_PPAGE:
1939             if (base_row <= 0) {
1940                 beep();
1941             } else {
1942                 base_row -= (page_size - 1);
1943                 if (base_row < 0)
1944                     base_row = 0;
1945             }
1946             break;
1947         case CTRL('f'):
1948         case KEY_NEXT:
1949         case KEY_NPAGE:
1950             if (base_row + page_size >= row_limit) {
1951                 beep();
1952             } else {
1953                 base_row += page_size - 1;
1954                 if (base_row + page_size >= row_limit) {
1955                     base_row = row_limit - page_size - 1;
1956                 }
1957             }
1958             break;
1959         case '?':
1960             if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
1961                 box(helpwin, 0, 0);
1962                 color_legend(helpwin);
1963                 wGetchar(helpwin);
1964                 delwin(helpwin);
1965             }
1966             break;
1967         default:
1968             beep();
1969             continue;
1970         }
1971     }
1972
1973     erase();
1974     endwin();
1975 }
1976
1977 #if USE_WIDEC_SUPPORT
1978 /* generate a color test pattern */
1979 static void
1980 wide_color_test(void)
1981 {
1982     int c;
1983     int i;
1984     int top = 0, width;
1985     int base_row = 0;
1986     int grid_top = top + 3;
1987     int page_size = (LINES - grid_top);
1988     int pairs_max = COLOR_PAIRS;
1989     int row_limit;
1990     int per_row;
1991     char numbered[80];
1992     const char *hello;
1993     bool done = FALSE;
1994     bool opt_bold = FALSE;
1995     bool opt_wide = FALSE;
1996     bool opt_nums = FALSE;
1997     WINDOW *helpwin;
1998
1999     while (!done) {
2000         int shown = 0;
2001
2002         /* this assumes an 80-column line */
2003         if (opt_wide) {
2004             width = 4;
2005             hello = "Test";
2006             per_row = (COLORS > 8) ? 16 : 8;
2007         } else {
2008             width = 8;
2009             hello = "Hello";
2010             per_row = 8;
2011         }
2012
2013         row_limit = (pairs_max + per_row - 1) / per_row;
2014
2015         move(0, 0);
2016         (void) printw("There are %d color pairs and %d colors\n",
2017                       pairs_max, COLORS);
2018
2019         clrtobot();
2020         (void) mvprintw(top + 1, 0,
2021                         "%dx%d matrix of foreground/background colors, bold *%s*\n",
2022                         row_limit,
2023                         per_row,
2024                         opt_bold ? "on" : "off");
2025
2026         /* show color names/numbers across the top */
2027         for (i = 0; i < per_row; i++)
2028             show_color_name(top + 2, (i + 1) * width, i, opt_wide);
2029
2030         /* show a grid of colors, with color names/ numbers on the left */
2031         for (i = (base_row * per_row); i < pairs_max; i++) {
2032             int row = grid_top + (i / per_row) - base_row;
2033             int col = (i % per_row + 1) * width;
2034             int pair = i;
2035
2036             if (row >= 0 && move(row, col) != ERR) {
2037                 init_pair(pair, i % COLORS, i / COLORS);
2038                 color_set(pair, NULL);
2039                 if (opt_bold)
2040                     attr_on((attr_t) A_BOLD, NULL);
2041
2042                 if (opt_nums) {
2043                     sprintf(numbered, "{%02X}", i);
2044                     hello = numbered;
2045                 }
2046                 printw("%-*.*s", width, width, hello);
2047                 attr_set(A_NORMAL, 0, NULL);
2048
2049                 if ((i % per_row) == 0 && (i % COLORS) == 0) {
2050                     show_color_name(row, 0, i / COLORS, opt_wide);
2051                 }
2052                 ++shown;
2053             } else if (shown) {
2054                 break;
2055             }
2056         }
2057
2058         switch (c = wGetchar(stdscr)) {
2059         case 'b':
2060             opt_bold = FALSE;
2061             break;
2062         case 'B':
2063             opt_bold = TRUE;
2064             break;
2065         case 'n':
2066             opt_nums = FALSE;
2067             break;
2068         case 'N':
2069             opt_nums = TRUE;
2070             break;
2071           case_QUIT:
2072             done = TRUE;
2073             continue;
2074         case 'w':
2075             set_color_test(opt_wide, FALSE);
2076             break;
2077         case 'W':
2078             set_color_test(opt_wide, TRUE);
2079             break;
2080         case CTRL('p'):
2081         case KEY_UP:
2082             if (base_row <= 0) {
2083                 beep();
2084             } else {
2085                 base_row -= 1;
2086             }
2087             break;
2088         case CTRL('n'):
2089         case KEY_DOWN:
2090             if (base_row + page_size >= row_limit) {
2091                 beep();
2092             } else {
2093                 base_row += 1;
2094             }
2095             break;
2096         case CTRL('b'):
2097         case KEY_PREVIOUS:
2098         case KEY_PPAGE:
2099             if (base_row <= 0) {
2100                 beep();
2101             } else {
2102                 base_row -= (page_size - 1);
2103                 if (base_row < 0)
2104                     base_row = 0;
2105             }
2106             break;
2107         case CTRL('f'):
2108         case KEY_NEXT:
2109         case KEY_NPAGE:
2110             if (base_row + page_size >= row_limit) {
2111                 beep();
2112             } else {
2113                 base_row += page_size - 1;
2114                 if (base_row + page_size >= row_limit) {
2115                     base_row = row_limit - page_size - 1;
2116                 }
2117             }
2118             break;
2119         case '?':
2120             if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
2121                 box(helpwin, 0, 0);
2122                 color_legend(helpwin);
2123                 wGetchar(helpwin);
2124                 delwin(helpwin);
2125             }
2126             break;
2127         default:
2128             beep();
2129             continue;
2130         }
2131     }
2132
2133     erase();
2134     endwin();
2135 }
2136 #endif /* USE_WIDEC_SUPPORT */
2137
2138 static void
2139 change_color(short current, int field, int value, int usebase)
2140 {
2141     short red, green, blue;
2142
2143     color_content(current, &red, &green, &blue);
2144
2145     switch (field) {
2146     case 0:
2147         red = usebase ? red + value : value;
2148         break;
2149     case 1:
2150         green = usebase ? green + value : value;
2151         break;
2152     case 2:
2153         blue = usebase ? blue + value : value;
2154         break;
2155     }
2156
2157     if (init_color(current, red, green, blue) == ERR)
2158         beep();
2159 }
2160
2161 static void
2162 init_all_colors(void)
2163 {
2164     short c;
2165
2166     for (c = 0; c < COLORS; ++c)
2167         init_color(c,
2168                    all_colors[c].red,
2169                    all_colors[c].green,
2170                    all_colors[c].blue);
2171 }
2172
2173 #define scaled_rgb(n) ((255 * (n)) / 1000)
2174
2175 static void
2176 color_edit(void)
2177 /* display the color test pattern, without trying to edit colors */
2178 {
2179     short i;
2180     short current = 0;
2181     int this_c = 0, value = 0, field = 0;
2182     int last_c;
2183     int top_color = 0;
2184     int page_size = (LINES - 6);
2185
2186     init_all_colors();
2187     refresh();
2188
2189     for (i = 0; i < max_colors; i++)
2190         init_pair(i, COLOR_WHITE, i);
2191
2192     mvprintw(LINES - 2, 0, "Number: %d", value);
2193
2194     do {
2195         short red, green, blue;
2196
2197         attron(A_BOLD);
2198         mvaddstr(0, 20, "Color RGB Value Editing");
2199         attroff(A_BOLD);
2200
2201         for (i = top_color;
2202              (i - top_color < page_size)
2203              && (i < max_colors); i++) {
2204             char numeric[80];
2205
2206             sprintf(numeric, "[%d]", i);
2207             mvprintw(2 + i - top_color, 0, "%c %-8s:",
2208                      (i == current ? '>' : ' '),
2209                      (i < (int) SIZEOF(the_color_names)
2210                       ? the_color_names[i] : numeric));
2211             attrset(COLOR_PAIR(i));
2212             addstr("        ");
2213             attrset(A_NORMAL);
2214
2215             color_content(i, &red, &green, &blue);
2216             addstr("   R = ");
2217             if (current == i && field == 0)
2218                 attron(A_STANDOUT);
2219             printw("%04d", red);
2220             if (current == i && field == 0)
2221                 attrset(A_NORMAL);
2222             addstr(", G = ");
2223             if (current == i && field == 1)
2224                 attron(A_STANDOUT);
2225             printw("%04d", green);
2226             if (current == i && field == 1)
2227                 attrset(A_NORMAL);
2228             addstr(", B = ");
2229             if (current == i && field == 2)
2230                 attron(A_STANDOUT);
2231             printw("%04d", blue);
2232             if (current == i && field == 2)
2233                 attrset(A_NORMAL);
2234             attrset(A_NORMAL);
2235             printw(" ( %3d %3d %3d )",
2236                    scaled_rgb(red),
2237                    scaled_rgb(green),
2238                    scaled_rgb(blue));
2239         }
2240
2241         mvaddstr(LINES - 3, 0,
2242                  "Use up/down to select a color, left/right to change fields.");
2243         mvaddstr(LINES - 2, 0,
2244                  "Modify field by typing nnn=, nnn-, or nnn+.  ? for help.");
2245
2246         move(2 + current - top_color, 0);
2247
2248         last_c = this_c;
2249         this_c = Getchar();
2250         if (this_c < 256 && isdigit(this_c) && !isdigit(last_c))
2251             value = 0;
2252
2253         switch (this_c) {
2254         case CTRL('b'):
2255         case KEY_PPAGE:
2256             if (current > 0)
2257                 current -= (page_size - 1);
2258             else
2259                 beep();
2260             break;
2261
2262         case CTRL('f'):
2263         case KEY_NPAGE:
2264             if (current < (max_colors - 1))
2265                 current += (page_size - 1);
2266             else
2267                 beep();
2268             break;
2269
2270         case CTRL('p'):
2271         case KEY_UP:
2272             current = (current == 0 ? (max_colors - 1) : current - 1);
2273             break;
2274
2275         case CTRL('n'):
2276         case KEY_DOWN:
2277             current = (current == (max_colors - 1) ? 0 : current + 1);
2278             break;
2279
2280         case KEY_RIGHT:
2281             field = (field == 2 ? 0 : field + 1);
2282             break;
2283
2284         case KEY_LEFT:
2285             field = (field == 0 ? 2 : field - 1);
2286             break;
2287
2288         case '0':
2289         case '1':
2290         case '2':
2291         case '3':
2292         case '4':
2293         case '5':
2294         case '6':
2295         case '7':
2296         case '8':
2297         case '9':
2298             value = value * 10 + (this_c - '0');
2299             break;
2300
2301         case '+':
2302             change_color(current, field, value, 1);
2303             break;
2304
2305         case '-':
2306             change_color(current, field, -value, 1);
2307             break;
2308
2309         case '=':
2310             change_color(current, field, value, 0);
2311             break;
2312
2313         case '?':
2314             erase();
2315             P("                      RGB Value Editing Help");
2316             P("");
2317             P("You are in the RGB value editor.  Use the arrow keys to select one of");
2318             P("the fields in one of the RGB triples of the current colors; the one");
2319             P("currently selected will be reverse-video highlighted.");
2320             P("");
2321             P("To change a field, enter the digits of the new value; they are echoed");
2322             P("as entered.  Finish by typing `='.  The change will take effect instantly.");
2323             P("To increment or decrement a value, use the same procedure, but finish");
2324             P("with a `+' or `-'.");
2325             P("");
2326             P("Press 'm' to invoke the top-level menu with the current color settings.");
2327             P("To quit, do ESC");
2328
2329             Pause();
2330             erase();
2331             break;
2332
2333         case 'm':
2334             endwin();
2335             main_menu(FALSE);
2336             refresh();
2337             break;
2338
2339           case_QUIT:
2340             break;
2341
2342         default:
2343             beep();
2344             break;
2345         }
2346
2347         if (current < 0)
2348             current = 0;
2349         if (current >= max_colors)
2350             current = max_colors - 1;
2351         if (current < top_color)
2352             top_color = current;
2353         if (current - top_color >= page_size)
2354             top_color = current - (page_size - 1);
2355
2356         mvprintw(LINES - 1, 0, "Number: %d", value);
2357         clrtoeol();
2358     } while
2359         (!isQuit(this_c));
2360
2361     erase();
2362
2363     /*
2364      * ncurses does not reset each color individually when calling endwin().
2365      */
2366     init_all_colors();
2367
2368     endwin();
2369 }
2370
2371 /****************************************************************************
2372  *
2373  * Soft-key label test
2374  *
2375  ****************************************************************************/
2376
2377 #if USE_SOFTKEYS
2378
2379 #define SLK_HELP 17
2380 #define SLK_WORK (SLK_HELP + 3)
2381
2382 static void
2383 slk_help(void)
2384 {
2385     static const char *table[] =
2386     {
2387         "Available commands are:"
2388         ,""
2389         ,"^L         -- repaint this message and activate soft keys"
2390         ,"a/d        -- activate/disable soft keys"
2391         ,"c          -- set centered format for labels"
2392         ,"l          -- set left-justified format for labels"
2393         ,"r          -- set right-justified format for labels"
2394         ,"[12345678] -- set label; labels are numbered 1 through 8"
2395         ,"e          -- erase stdscr (should not erase labels)"
2396         ,"s          -- test scrolling of shortened screen"
2397 #if HAVE_SLK_COLOR
2398         ,"F/B        -- cycle through foreground/background colors"
2399 #endif
2400         ,"ESC  -- return to main menu"
2401         ,""
2402         ,"Note: if activating the soft keys causes your terminal to scroll up"
2403         ,"one line, your terminal auto-scrolls when anything is written to the"
2404         ,"last screen position.  The ncurses code does not yet handle this"
2405         ,"gracefully."
2406     };
2407     unsigned j;
2408
2409     move(2, 0);
2410     for (j = 0; j < SIZEOF(table); ++j) {
2411         P(table[j]);
2412     }
2413     refresh();
2414 }
2415
2416 #if HAVE_SLK_COLOR
2417 static void
2418 call_slk_color(short fg, short bg)
2419 {
2420     init_pair(1, bg, fg);
2421     slk_color(1);
2422     mvprintw(SLK_WORK, 0, "Colors %d/%d\n", fg, bg);
2423     clrtoeol();
2424     refresh();
2425 }
2426 #endif
2427
2428 static void
2429 slk_test(void)
2430 /* exercise the soft keys */
2431 {
2432     int c, fmt = 1;
2433     char buf[9];
2434     char *s;
2435 #if HAVE_SLK_COLOR
2436     short fg = COLOR_BLACK;
2437     short bg = COLOR_WHITE;
2438 #endif
2439
2440     c = CTRL('l');
2441 #if HAVE_SLK_COLOR
2442     if (use_colors) {
2443         call_slk_color(fg, bg);
2444     }
2445 #endif
2446
2447     do {
2448         move(0, 0);
2449         switch (c) {
2450         case CTRL('l'):
2451             erase();
2452             attron(A_BOLD);
2453             mvaddstr(0, 20, "Soft Key Exerciser");
2454             attroff(A_BOLD);
2455
2456             slk_help();
2457             /* fall through */
2458
2459         case 'a':
2460             slk_restore();
2461             break;
2462
2463         case 'e':
2464             wclear(stdscr);
2465             break;
2466
2467         case 's':
2468             mvprintw(SLK_WORK, 0, "Press Q to stop the scrolling-test: ");
2469             while ((c = Getchar()) != 'Q' && (c != ERR))
2470                 addch((chtype) c);
2471             break;
2472
2473         case 'd':
2474             slk_clear();
2475             break;
2476
2477         case 'l':
2478             fmt = 0;
2479             break;
2480
2481         case 'c':
2482             fmt = 1;
2483             break;
2484
2485         case 'r':
2486             fmt = 2;
2487             break;
2488
2489         case '1':
2490         case '2':
2491         case '3':
2492         case '4':
2493         case '5':
2494         case '6':
2495         case '7':
2496         case '8':
2497             (void) mvaddstr(SLK_WORK, 0, "Please enter the label value: ");
2498             strcpy(buf, "");
2499             if ((s = slk_label(c - '0')) != 0) {
2500                 strncpy(buf, s, 8);
2501             }
2502             wGetstring(stdscr, buf, 8);
2503             slk_set((c - '0'), buf, fmt);
2504             slk_refresh();
2505             move(SLK_WORK, 0);
2506             clrtobot();
2507             break;
2508
2509           case_QUIT:
2510             goto done;
2511
2512 #if HAVE_SLK_COLOR
2513         case 'F':
2514             if (use_colors) {
2515                 fg = (fg + 1) % COLORS;
2516                 call_slk_color(fg, bg);
2517             }
2518             break;
2519         case 'B':
2520             if (use_colors) {
2521                 bg = (bg + 1) % COLORS;
2522                 call_slk_color(fg, bg);
2523             }
2524             break;
2525 #endif
2526 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
2527         case KEY_RESIZE:
2528             break;
2529 #endif
2530
2531         default:
2532             beep();
2533         }
2534     } while (!isQuit(c = Getchar()));
2535
2536   done:
2537     slk_clear();
2538     erase();
2539     endwin();
2540 }
2541
2542 #if USE_WIDEC_SUPPORT
2543 #define SLKLEN 8
2544 static void
2545 wide_slk_test(void)
2546 /* exercise the soft keys */
2547 {
2548     int c, fmt = 1;
2549     wchar_t buf[SLKLEN + 1];
2550     char *s;
2551     short fg = COLOR_BLACK;
2552     short bg = COLOR_WHITE;
2553
2554     c = CTRL('l');
2555     if (use_colors) {
2556         call_slk_color(fg, bg);
2557     }
2558     do {
2559         move(0, 0);
2560         switch (c) {
2561         case CTRL('l'):
2562             erase();
2563             attr_on(WA_BOLD, NULL);
2564             mvaddstr(0, 20, "Soft Key Exerciser");
2565             attr_off(WA_BOLD, NULL);
2566
2567             slk_help();
2568             /* fall through */
2569
2570         case 'a':
2571             slk_restore();
2572             break;
2573
2574         case 'e':
2575             wclear(stdscr);
2576             break;
2577
2578         case 's':
2579             mvprintw(SLK_WORK, 0, "Press Q to stop the scrolling-test: ");
2580             while ((c = Getchar()) != 'Q' && (c != ERR))
2581                 addch((chtype) c);
2582             break;
2583
2584         case 'd':
2585             slk_clear();
2586             break;
2587
2588         case 'l':
2589             fmt = 0;
2590             break;
2591
2592         case 'c':
2593             fmt = 1;
2594             break;
2595
2596         case 'r':
2597             fmt = 2;
2598             break;
2599
2600         case '1':
2601         case '2':
2602         case '3':
2603         case '4':
2604         case '5':
2605         case '6':
2606         case '7':
2607         case '8':
2608             (void) mvaddstr(SLK_WORK, 0, "Please enter the label value: ");
2609             *buf = 0;
2610             if ((s = slk_label(c - '0')) != 0) {
2611                 char *temp = strdup(s);
2612                 size_t used = strlen(temp);
2613                 size_t want = SLKLEN;
2614                 size_t test;
2615                 mbstate_t state;
2616
2617                 buf[0] = L'\0';
2618                 while (want > 0 && used != 0) {
2619                     const char *base = s;
2620                     memset(&state, 0, sizeof(state));
2621                     test = mbsrtowcs(0, &base, 0, &state);
2622                     if (test == (size_t) -1) {
2623                         temp[--used] = 0;
2624                     } else if (test > want) {
2625                         temp[--used] = 0;
2626                     } else {
2627                         memset(&state, 0, sizeof(state));
2628                         mbsrtowcs(buf, &base, want, &state);
2629                         break;
2630                     }
2631                 }
2632                 free(temp);
2633             }
2634             wGet_wstring(stdscr, buf, SLKLEN);
2635             slk_wset((c - '0'), buf, fmt);
2636             slk_refresh();
2637             move(SLK_WORK, 0);
2638             clrtobot();
2639             break;
2640
2641           case_QUIT:
2642             goto done;
2643
2644         case 'F':
2645             if (use_colors) {
2646                 fg = (fg + 1) % COLORS;
2647                 call_slk_color(fg, bg);
2648             }
2649             break;
2650         case 'B':
2651             if (use_colors) {
2652                 bg = (bg + 1) % COLORS;
2653                 call_slk_color(fg, bg);
2654             }
2655             break;
2656 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
2657         case KEY_RESIZE:
2658             break;
2659 #endif
2660         default:
2661             beep();
2662         }
2663     } while (!isQuit(c = Getchar()));
2664
2665   done:
2666     slk_clear();
2667     erase();
2668     endwin();
2669 }
2670 #endif
2671 #endif /* SLK_INIT */
2672
2673 /****************************************************************************
2674  *
2675  * Alternate character-set stuff
2676  *
2677  ****************************************************************************/
2678
2679 /* ISO 6429:  codes 0x80 to 0x9f may be control characters that cause the
2680  * terminal to perform functions.  The remaining codes can be graphic.
2681  */
2682 static void
2683 show_upper_chars(unsigned first)
2684 {
2685     bool C1 = (first == 128);
2686     unsigned code;
2687     unsigned last = first + 31;
2688     int reply;
2689
2690     erase();
2691     attron(A_BOLD);
2692     mvprintw(0, 20, "Display of %s Character Codes %d to %d",
2693              C1 ? "C1" : "GR", first, last);
2694     attroff(A_BOLD);
2695     refresh();
2696
2697     for (code = first; code <= last; code++) {
2698         int row = 2 + ((code - first) % 16);
2699         int col = ((code - first) / 16) * COLS / 2;
2700         char tmp[80];
2701         sprintf(tmp, "%3u (0x%x)", code, code);
2702         mvprintw(row, col, "%*s: ", COLS / 4, tmp);
2703         if (C1)
2704             nodelay(stdscr, TRUE);
2705         echochar(code);
2706         if (C1) {
2707             /* (yes, this _is_ crude) */
2708             while ((reply = Getchar()) != ERR) {
2709                 addch(UChar(reply));
2710                 napms(10);
2711             }
2712             nodelay(stdscr, FALSE);
2713         }
2714     }
2715 }
2716
2717 static void
2718 show_pc_chars(void)
2719 {
2720     unsigned code;
2721
2722     erase();
2723     attron(A_BOLD);
2724     mvprintw(0, 20, "Display of PC Character Codes");
2725     attroff(A_BOLD);
2726     refresh();
2727
2728     for (code = 0; code < 16; ++code) {
2729         mvprintw(2, (int) code * 3 + 8, "%X", code);
2730     }
2731     for (code = 0; code < 256; code++) {
2732         int row = 3 + (code / 16) + (code >= 128);
2733         int col = 8 + (code % 16) * 3;
2734         if ((code % 16) == 0)
2735             mvprintw(row, 0, "0x%02x:", code);
2736         move(row, col);
2737         switch (code) {
2738         case '\n':
2739         case '\r':
2740         case '\b':
2741         case '\f':
2742         case '\033':
2743         case 0x9b:
2744             /*
2745              * Skip the ones that do not work.
2746              */
2747             break;
2748         default:
2749             addch(code | A_ALTCHARSET);
2750             break;
2751         }
2752     }
2753 }
2754
2755 static void
2756 show_box_chars(void)
2757 {
2758     erase();
2759     attron(A_BOLD);
2760     mvaddstr(0, 20, "Display of the ACS Line-Drawing Set");
2761     attroff(A_BOLD);
2762     refresh();
2763     box(stdscr, 0, 0);
2764     /* *INDENT-OFF* */
2765     mvhline(LINES / 2, 0,        ACS_HLINE, COLS);
2766     mvvline(0,         COLS / 2, ACS_VLINE, LINES);
2767     mvaddch(0,         COLS / 2, ACS_TTEE);
2768     mvaddch(LINES / 2, COLS / 2, ACS_PLUS);
2769     mvaddch(LINES - 1, COLS / 2, ACS_BTEE);
2770     mvaddch(LINES / 2, 0,        ACS_LTEE);
2771     mvaddch(LINES / 2, COLS - 1, ACS_RTEE);
2772     /* *INDENT-ON* */
2773
2774 }
2775
2776 static int
2777 show_1_acs(int n, const char *name, chtype code)
2778 {
2779     const int height = 16;
2780     int row = 2 + (n % height);
2781     int col = (n / height) * COLS / 2;
2782     mvprintw(row, col, "%*s : ", COLS / 4, name);
2783     addch(code);
2784     return n + 1;
2785 }
2786
2787 static void
2788 show_acs_chars(void)
2789 /* display the ACS character set */
2790 {
2791     int n;
2792
2793 #define BOTH(name) #name, name
2794
2795     erase();
2796     attron(A_BOLD);
2797     mvaddstr(0, 20, "Display of the ACS Character Set");
2798     attroff(A_BOLD);
2799     refresh();
2800
2801     n = show_1_acs(0, BOTH(ACS_ULCORNER));
2802     n = show_1_acs(n, BOTH(ACS_URCORNER));
2803     n = show_1_acs(n, BOTH(ACS_LLCORNER));
2804     n = show_1_acs(n, BOTH(ACS_LRCORNER));
2805
2806     n = show_1_acs(n, BOTH(ACS_LTEE));
2807     n = show_1_acs(n, BOTH(ACS_RTEE));
2808     n = show_1_acs(n, BOTH(ACS_TTEE));
2809     n = show_1_acs(n, BOTH(ACS_BTEE));
2810
2811     n = show_1_acs(n, BOTH(ACS_HLINE));
2812     n = show_1_acs(n, BOTH(ACS_VLINE));
2813
2814     /*
2815      * HPUX's ACS definitions are broken here.  Just give up.
2816      */
2817 #if !(defined(__hpux) && !defined(NCURSES_VERSION))
2818     n = show_1_acs(n, BOTH(ACS_LARROW));
2819     n = show_1_acs(n, BOTH(ACS_RARROW));
2820     n = show_1_acs(n, BOTH(ACS_UARROW));
2821     n = show_1_acs(n, BOTH(ACS_DARROW));
2822
2823     n = show_1_acs(n, BOTH(ACS_BLOCK));
2824     n = show_1_acs(n, BOTH(ACS_BOARD));
2825     n = show_1_acs(n, BOTH(ACS_LANTERN));
2826     n = show_1_acs(n, BOTH(ACS_BULLET));
2827     n = show_1_acs(n, BOTH(ACS_CKBOARD));
2828     n = show_1_acs(n, BOTH(ACS_DEGREE));
2829     n = show_1_acs(n, BOTH(ACS_DIAMOND));
2830     n = show_1_acs(n, BOTH(ACS_PLMINUS));
2831     n = show_1_acs(n, BOTH(ACS_PLUS));
2832
2833     n = show_1_acs(n, BOTH(ACS_GEQUAL));
2834     n = show_1_acs(n, BOTH(ACS_NEQUAL));
2835     n = show_1_acs(n, BOTH(ACS_LEQUAL));
2836
2837     n = show_1_acs(n, BOTH(ACS_STERLING));
2838     n = show_1_acs(n, BOTH(ACS_PI));
2839     n = show_1_acs(n, BOTH(ACS_S1));
2840     n = show_1_acs(n, BOTH(ACS_S3));
2841     n = show_1_acs(n, BOTH(ACS_S7));
2842     n = show_1_acs(n, BOTH(ACS_S9));
2843 #endif
2844 }
2845
2846 static void
2847 acs_display(void)
2848 {
2849     int c = 'a';
2850     char *term = getenv("TERM");
2851     const char *pch_kludge = ((term != 0 && strstr(term, "linux"))
2852                               ? "p=PC, "
2853                               : "");
2854
2855     do {
2856         switch (c) {
2857         case CTRL('L'):
2858             Repaint();
2859             break;
2860         case 'a':
2861             show_acs_chars();
2862             break;
2863         case 'x':
2864             show_box_chars();
2865             break;
2866         case '0':
2867         case '1':
2868         case '2':
2869         case '3':
2870             show_upper_chars((unsigned) ((c - '0') * 32 + 128));
2871             break;
2872         case 'p':
2873             show_pc_chars();
2874             break;
2875         default:
2876             beep();
2877             break;
2878         }
2879         mvprintw(LINES - 3, 0,
2880                  "Note: ANSI terminals may not display C1 characters.");
2881         mvprintw(LINES - 2, 0,
2882                  "Select: a=ACS, x=box, %s0=C1, 1,2,3=GR characters, ESC=quit",
2883                  pch_kludge);
2884         refresh();
2885     } while (!isQuit(c = Getchar()));
2886
2887     Pause();
2888     erase();
2889     endwin();
2890 }
2891
2892 #if USE_WIDEC_SUPPORT
2893 static cchar_t *
2894 merge_wide_attr(cchar_t *dst, cchar_t *src, attr_t attr, short pair)
2895 {
2896     int count = getcchar(src, NULL, NULL, NULL, 0);
2897     wchar_t *wch = 0;
2898     attr_t ignore_attr;
2899     short ignore_pair;
2900
2901     *dst = *src;
2902     if (count > 0) {
2903         if ((wch = typeMalloc(wchar_t, count)) != 0) {
2904             if (getcchar(src, wch, &ignore_attr, &ignore_pair, 0) != ERR) {
2905                 attr |= (ignore_attr & A_ALTCHARSET);
2906                 setcchar(dst, wch, attr, pair, 0);
2907             }
2908             free(wch);
2909         }
2910     }
2911     return dst;
2912 }
2913
2914 static void
2915 show_upper_widechars(int first, int repeat, int space, attr_t attr, short pair)
2916 {
2917     cchar_t temp;
2918     wchar_t code;
2919     int last = first + 31;
2920
2921     erase();
2922     attron(A_BOLD);
2923     mvprintw(0, 20, "Display of Character Codes %d to %d", first, last);
2924     attroff(A_BOLD);
2925
2926     for (code = first; code <= last; code++) {
2927         int row = 2 + ((code - first) % 16);
2928         int col = ((code - first) / 16) * COLS / 2;
2929         wchar_t codes[10];
2930         char tmp[80];
2931         int count = repeat;
2932         int y, x;
2933
2934         memset(&codes, 0, sizeof(codes));
2935         codes[0] = code;
2936         sprintf(tmp, "%3ld (0x%lx)", (long) code, (long) code);
2937         mvprintw(row, col, "%*s: ", COLS / 4, tmp);
2938         setcchar(&temp, codes, attr, pair, 0);
2939         do {
2940             /*
2941              * Give non-spacing characters something to combine with.  If we
2942              * don't, they'll bunch up in a heap on the space after the ":".
2943              * Mark them with reverse-video to make them simpler to find on
2944              * the display.
2945              */
2946             if (wcwidth(code) == 0)
2947                 addch(space | A_REVERSE);
2948             /*
2949              * This could use add_wch(), but is done for comparison with the
2950              * normal 'f' test (and to make a test-case for echo_wchar()).
2951              * The screen will flicker because the erase() at the top of the
2952              * function is met by the builtin refresh() in echo_wchar().
2953              */
2954             echo_wchar(&temp);
2955             /*
2956              * The repeat-count may make text wrap - avoid that.
2957              */
2958             getyx(stdscr, y, x);
2959             if (x >= col + (COLS / 2) - 2)
2960                 break;
2961         } while (--count > 0);
2962     }
2963 }
2964
2965 static int
2966 show_1_wacs(int n, const char *name, const cchar_t *code)
2967 {
2968     const int height = 16;
2969     int row = 2 + (n % height);
2970     int col = (n / height) * COLS / 2;
2971     mvprintw(row, col, "%*s : ", COLS / 4, name);
2972     add_wchnstr(code, 1);
2973     return n + 1;
2974 }
2975
2976 #define MERGE_ATTR(wch) merge_wide_attr(&temp, wch, attr, pair)
2977
2978 static void
2979 show_wacs_chars(attr_t attr, short pair)
2980 /* display the wide-ACS character set */
2981 {
2982     cchar_t temp;
2983
2984     int n;
2985
2986 /*#define BOTH2(name) #name, &(name) */
2987 #define BOTH2(name) #name, MERGE_ATTR(name)
2988
2989     erase();
2990     attron(A_BOLD);
2991     mvaddstr(0, 20, "Display of the Wide-ACS Character Set");
2992     attroff(A_BOLD);
2993     refresh();
2994
2995     n = show_1_wacs(0, BOTH2(WACS_ULCORNER));
2996     n = show_1_wacs(n, BOTH2(WACS_URCORNER));
2997     n = show_1_wacs(n, BOTH2(WACS_LLCORNER));
2998     n = show_1_wacs(n, BOTH2(WACS_LRCORNER));
2999
3000     n = show_1_wacs(n, BOTH2(WACS_LTEE));
3001     n = show_1_wacs(n, BOTH2(WACS_RTEE));
3002     n = show_1_wacs(n, BOTH2(WACS_TTEE));
3003     n = show_1_wacs(n, BOTH2(WACS_BTEE));
3004
3005     n = show_1_wacs(n, BOTH2(WACS_HLINE));
3006     n = show_1_wacs(n, BOTH2(WACS_VLINE));
3007
3008     n = show_1_wacs(n, BOTH2(WACS_LARROW));
3009     n = show_1_wacs(n, BOTH2(WACS_RARROW));
3010     n = show_1_wacs(n, BOTH2(WACS_UARROW));
3011     n = show_1_wacs(n, BOTH2(WACS_DARROW));
3012
3013     n = show_1_wacs(n, BOTH2(WACS_BLOCK));
3014     n = show_1_wacs(n, BOTH2(WACS_BOARD));
3015     n = show_1_wacs(n, BOTH2(WACS_LANTERN));
3016     n = show_1_wacs(n, BOTH2(WACS_BULLET));
3017     n = show_1_wacs(n, BOTH2(WACS_CKBOARD));
3018     n = show_1_wacs(n, BOTH2(WACS_DEGREE));
3019     n = show_1_wacs(n, BOTH2(WACS_DIAMOND));
3020     n = show_1_wacs(n, BOTH2(WACS_PLMINUS));
3021     n = show_1_wacs(n, BOTH2(WACS_PLUS));
3022
3023 #ifdef CURSES_WACS_ARRAY
3024     n = show_1_wacs(n, BOTH2(WACS_GEQUAL));
3025     n = show_1_wacs(n, BOTH2(WACS_NEQUAL));
3026     n = show_1_wacs(n, BOTH2(WACS_LEQUAL));
3027
3028     n = show_1_wacs(n, BOTH2(WACS_STERLING));
3029     n = show_1_wacs(n, BOTH2(WACS_PI));
3030     n = show_1_wacs(n, BOTH2(WACS_S1));
3031     n = show_1_wacs(n, BOTH2(WACS_S3));
3032     n = show_1_wacs(n, BOTH2(WACS_S7));
3033     n = show_1_wacs(n, BOTH2(WACS_S9));
3034 #endif
3035 }
3036
3037 #undef MERGE_ATTR
3038
3039 #define MERGE_ATTR(wch) merge_wide_attr(&temp, wch, attr, pair)
3040
3041 static void
3042 show_wbox_chars(attr_t attr, short pair)
3043 {
3044     cchar_t temp;
3045
3046     erase();
3047     attron(A_BOLD);
3048     mvaddstr(0, 20, "Display of the Wide-ACS Line-Drawing Set");
3049     attroff(A_BOLD);
3050     refresh();
3051
3052     attr_set(attr, pair, 0);
3053     box_set(stdscr, 0, 0);
3054     attr_set(A_NORMAL, 0, 0);
3055     /* *INDENT-OFF* */
3056     mvhline_set(LINES / 2, 0,        MERGE_ATTR(WACS_HLINE), COLS);
3057     mvvline_set(0,         COLS / 2, MERGE_ATTR(WACS_VLINE), LINES);
3058     mvadd_wch(0,           COLS / 2, MERGE_ATTR(WACS_TTEE));
3059     mvadd_wch(LINES / 2,   COLS / 2, MERGE_ATTR(WACS_PLUS));
3060     mvadd_wch(LINES - 1,   COLS / 2, MERGE_ATTR(WACS_BTEE));
3061     mvadd_wch(LINES / 2,   0,        MERGE_ATTR(WACS_LTEE));
3062     mvadd_wch(LINES / 2,   COLS - 1, MERGE_ATTR(WACS_RTEE));
3063     /* *INDENT-ON* */
3064
3065 }
3066
3067 #undef MERGE_ATTR
3068
3069 static int
3070 show_2_wacs(int n, const char *name, const char *code, attr_t attr, short pair)
3071 {
3072     const int height = 16;
3073     int row = 2 + (n % height);
3074     int col = (n / height) * COLS / 2;
3075     char temp[80];
3076
3077     mvprintw(row, col, "%*s : ", COLS / 4, name);
3078     attr_set(attr, pair, 0);
3079     addstr(strcpy(temp, code));
3080     attr_set(A_NORMAL, 0, 0);
3081     return n + 1;
3082 }
3083
3084 #define SHOW_UTF8(n, name, code) show_2_wacs(n, name, code, attr, pair)
3085
3086 static void
3087 show_utf8_chars(attr_t attr, short pair)
3088 {
3089     int n;
3090
3091     erase();
3092     attron(A_BOLD);
3093     mvaddstr(0, 20, "Display of the Wide-ACS Character Set");
3094     attroff(A_BOLD);
3095     refresh();
3096     /* *INDENT-OFF* */
3097     n = SHOW_UTF8(0, "WACS_ULCORNER",   "\342\224\214");
3098     n = SHOW_UTF8(n, "WACS_URCORNER",   "\342\224\220");
3099     n = SHOW_UTF8(n, "WACS_LLCORNER",   "\342\224\224");
3100     n = SHOW_UTF8(n, "WACS_LRCORNER",   "\342\224\230");
3101
3102     n = SHOW_UTF8(n, "WACS_LTEE",       "\342\224\234");
3103     n = SHOW_UTF8(n, "WACS_RTEE",       "\342\224\244");
3104     n = SHOW_UTF8(n, "WACS_TTEE",       "\342\224\254");
3105     n = SHOW_UTF8(n, "WACS_BTEE",       "\342\224\264");
3106
3107     n = SHOW_UTF8(n, "WACS_HLINE",      "\342\224\200");
3108     n = SHOW_UTF8(n, "WACS_VLINE",      "\342\224\202");
3109
3110     n = SHOW_UTF8(n, "WACS_LARROW",     "\342\206\220");
3111     n = SHOW_UTF8(n, "WACS_RARROW",     "\342\206\222");
3112     n = SHOW_UTF8(n, "WACS_UARROW",     "\342\206\221");
3113     n = SHOW_UTF8(n, "WACS_DARROW",     "\342\206\223");
3114
3115     n = SHOW_UTF8(n, "WACS_BLOCK",      "\342\226\256");
3116     n = SHOW_UTF8(n, "WACS_BOARD",      "\342\226\222");
3117     n = SHOW_UTF8(n, "WACS_LANTERN",    "\342\230\203");
3118     n = SHOW_UTF8(n, "WACS_BULLET",     "\302\267");
3119     n = SHOW_UTF8(n, "WACS_CKBOARD",    "\342\226\222");
3120     n = SHOW_UTF8(n, "WACS_DEGREE",     "\302\260");
3121     n = SHOW_UTF8(n, "WACS_DIAMOND",    "\342\227\206");
3122     n = SHOW_UTF8(n, "WACS_PLMINUS",    "\302\261");
3123     n = SHOW_UTF8(n, "WACS_PLUS",       "\342\224\274");
3124     n = SHOW_UTF8(n, "WACS_GEQUAL",     "\342\211\245");
3125     n = SHOW_UTF8(n, "WACS_NEQUAL",     "\342\211\240");
3126     n = SHOW_UTF8(n, "WACS_LEQUAL",     "\342\211\244");
3127
3128     n = SHOW_UTF8(n, "WACS_STERLING",   "\302\243");
3129     n = SHOW_UTF8(n, "WACS_PI",         "\317\200");
3130     n = SHOW_UTF8(n, "WACS_S1",         "\342\216\272");
3131     n = SHOW_UTF8(n, "WACS_S3",         "\342\216\273");
3132     n = SHOW_UTF8(n, "WACS_S7",         "\342\216\274");
3133     n = SHOW_UTF8(n, "WACS_S9",         "\342\216\275");
3134     /* *INDENT-ON* */
3135
3136 }
3137 /* *INDENT-OFF* */
3138 static struct {
3139     attr_t attr;
3140     const char *name;
3141 } attrs_to_cycle[] = {
3142     { A_NORMAL,         "normal" },
3143     { A_BOLD,           "bold" },
3144     { A_REVERSE,        "reverse" },
3145     { A_UNDERLINE,      "underline" },
3146 };
3147 /* *INDENT-ON* */
3148
3149 static bool
3150 cycle_attr(int ch, unsigned *at_code, chtype *attr)
3151 {
3152     bool result = TRUE;
3153
3154     switch (ch) {
3155     case 'v':
3156         if ((*at_code += 1) >= SIZEOF(attrs_to_cycle))
3157             *at_code = 0;
3158         break;
3159     case 'V':
3160         if (*at_code == 1)
3161             *at_code = SIZEOF(attrs_to_cycle) - 1;
3162         else
3163             *at_code -= 1;
3164         break;
3165     default:
3166         result = FALSE;
3167         break;
3168     }
3169     if (result)
3170         *attr = attrs_to_cycle[*at_code].attr;
3171     return result;
3172 }
3173
3174 static bool
3175 cycle_colors(int ch, int *fg, int *bg, short *pair)
3176 {
3177     bool result = FALSE;
3178
3179     if (use_colors) {
3180         result = TRUE;
3181         switch (ch) {
3182         case 'F':
3183             if ((*fg -= 1) < 0)
3184                 *fg = COLORS - 1;
3185             break;
3186         case 'f':
3187             if ((*fg += 1) >= COLORS)
3188                 *fg = 0;
3189             break;
3190         case 'B':
3191             if ((*bg -= 1) < 0)
3192                 *bg = COLORS - 1;
3193             break;
3194         case 'b':
3195             if ((*bg += 1) < 0)
3196                 *bg = 0;
3197             break;
3198         default:
3199             result = FALSE;
3200             break;
3201         }
3202         if (result) {
3203             *pair = (*fg != COLOR_BLACK || *bg != COLOR_BLACK);
3204             if (*pair != 0) {
3205                 *pair = 1;
3206                 if (init_pair(*pair, *fg, *bg) == ERR) {
3207                     result = FALSE;
3208                 }
3209             }
3210         }
3211     }
3212     return result;
3213 }
3214
3215 /* display the wide-ACS character set */
3216 static void
3217 wide_acs_display(void)
3218 {
3219     int c = 'a';
3220     int digit = 0;
3221     int repeat = 0;
3222     int space = ' ';
3223     chtype attr = A_NORMAL;
3224     int fg = COLOR_BLACK;
3225     int bg = COLOR_BLACK;
3226     unsigned at_code = 0;
3227     short pair = 0;
3228     void (*last_show_wacs) (attr_t, short) = 0;
3229
3230     do {
3231         switch (c) {
3232         case CTRL('L'):
3233             Repaint();
3234             break;
3235         case 'a':
3236             last_show_wacs = show_wacs_chars;
3237             break;
3238         case 'x':
3239             last_show_wacs = show_wbox_chars;
3240             break;
3241         case 'u':
3242             last_show_wacs = show_utf8_chars;
3243             break;
3244         default:
3245             if (c < 256 && isdigit(c)) {
3246                 digit = (c - '0');
3247             } else if (c == '+') {
3248                 ++digit;
3249             } else if (c == '-' && digit > 0) {
3250                 --digit;
3251             } else if (c == '>' && repeat < (COLS / 4)) {
3252                 ++repeat;
3253             } else if (c == '<' && repeat > 0) {
3254                 --repeat;
3255             } else if (c == '_') {
3256                 space = (space == ' ') ? '_' : ' ';
3257             } else if (cycle_attr(c, &at_code, &attr)
3258                        || cycle_colors(c, &fg, &bg, &pair)) {
3259                 if (last_show_wacs != 0)
3260                     break;
3261             } else {
3262                 beep();
3263                 break;
3264             }
3265             last_show_wacs = 0;
3266             show_upper_widechars(digit * 32 + 128, repeat, space, attr, pair);
3267             break;
3268         }
3269         if (last_show_wacs != 0)
3270             last_show_wacs(attr, pair);
3271
3272         mvprintw(LINES - 3, 0,
3273                  "Select: a WACS, x box, u UTF-8, 0-9,+/- non-ASCII, </> repeat, ESC=quit");
3274         if (use_colors) {
3275             mvprintw(LINES - 2, 0,
3276                      "v/V, f/F, b/B cycle through video attributes (%s) and color %d/%d.",
3277                      attrs_to_cycle[at_code].name,
3278                      fg, bg);
3279         } else {
3280             mvprintw(LINES - 2, 0,
3281                      "v/V cycles through video attributes (%s).",
3282                      attrs_to_cycle[at_code].name);
3283         }
3284         refresh();
3285     } while (!isQuit(c = Getchar()));
3286
3287     Pause();
3288     erase();
3289     endwin();
3290 }
3291
3292 #endif
3293
3294 /*
3295  * Graphic-rendition test (adapted from vttest)
3296  */
3297 static void
3298 test_sgr_attributes(void)
3299 {
3300     int pass;
3301
3302     for (pass = 0; pass < 2; pass++) {
3303         chtype normal = ((pass == 0 ? A_NORMAL : A_REVERSE)) | BLANK;
3304
3305         /* Use non-default colors if possible to exercise bce a little */
3306         if (use_colors) {
3307             init_pair(1, COLOR_WHITE, COLOR_BLUE);
3308             normal |= COLOR_PAIR(1);
3309         }
3310         bkgdset(normal);
3311         erase();
3312         mvprintw(1, 20, "Graphic rendition test pattern:");
3313
3314         mvprintw(4, 1, "vanilla");
3315
3316 #define set_sgr(mask) bkgdset((normal^(mask)));
3317         set_sgr(A_BOLD);
3318         mvprintw(4, 40, "bold");
3319
3320         set_sgr(A_UNDERLINE);
3321         mvprintw(6, 6, "underline");
3322
3323         set_sgr(A_BOLD | A_UNDERLINE);
3324         mvprintw(6, 45, "bold underline");
3325
3326         set_sgr(A_BLINK);
3327         mvprintw(8, 1, "blink");
3328
3329         set_sgr(A_BLINK | A_BOLD);
3330         mvprintw(8, 40, "bold blink");
3331
3332         set_sgr(A_UNDERLINE | A_BLINK);
3333         mvprintw(10, 6, "underline blink");
3334
3335         set_sgr(A_BOLD | A_UNDERLINE | A_BLINK);
3336         mvprintw(10, 45, "bold underline blink");
3337
3338         set_sgr(A_REVERSE);
3339         mvprintw(12, 1, "negative");
3340
3341         set_sgr(A_BOLD | A_REVERSE);
3342         mvprintw(12, 40, "bold negative");
3343
3344         set_sgr(A_UNDERLINE | A_REVERSE);
3345         mvprintw(14, 6, "underline negative");
3346
3347         set_sgr(A_BOLD | A_UNDERLINE | A_REVERSE);
3348         mvprintw(14, 45, "bold underline negative");
3349
3350         set_sgr(A_BLINK | A_REVERSE);
3351         mvprintw(16, 1, "blink negative");
3352
3353         set_sgr(A_BOLD | A_BLINK | A_REVERSE);
3354         mvprintw(16, 40, "bold blink negative");
3355
3356         set_sgr(A_UNDERLINE | A_BLINK | A_REVERSE);
3357         mvprintw(18, 6, "underline blink negative");
3358
3359         set_sgr(A_BOLD | A_UNDERLINE | A_BLINK | A_REVERSE);
3360         mvprintw(18, 45, "bold underline blink negative");
3361
3362         bkgdset(normal);
3363         mvprintw(LINES - 2, 1, "%s background. ", pass == 0 ? "Dark" :
3364                  "Light");
3365         clrtoeol();
3366         Pause();
3367     }
3368
3369     bkgdset(A_NORMAL | BLANK);
3370     erase();
3371     endwin();
3372 }
3373
3374 /****************************************************************************
3375  *
3376  * Windows and scrolling tester.
3377  *
3378  ****************************************************************************/
3379
3380 #define BOTLINES        4       /* number of line stolen from screen bottom */
3381
3382 typedef struct {
3383     int y, x;
3384 } pair;
3385
3386 #define FRAME struct frame
3387 FRAME
3388 {
3389     FRAME *next, *last;
3390     bool do_scroll;
3391     bool do_keypad;
3392     WINDOW *wind;
3393 };
3394
3395 #if defined(NCURSES_VERSION)
3396 #if NCURSES_VERSION_PATCH < 20070331
3397 #define is_keypad(win)   (win)->_use_keypad
3398 #define is_scrollok(win) (win)->_scroll
3399 #endif
3400 #else
3401 #define is_keypad(win)   FALSE
3402 #define is_scrollok(win) FALSE
3403 #endif
3404
3405 /* We need to know if these flags are actually set, so don't look in FRAME.
3406  * These names are known to work with SVr4 curses as well as ncurses.  The
3407  * _use_keypad name does not work with Solaris 8.
3408  */
3409 static bool
3410 HaveKeypad(FRAME * curp)
3411 {
3412     WINDOW *win = (curp ? curp->wind : stdscr);
3413     (void) win;
3414     return is_keypad(win);
3415 }
3416
3417 static bool
3418 HaveScroll(FRAME * curp)
3419 {
3420     WINDOW *win = (curp ? curp->wind : stdscr);
3421     (void) win;
3422     return is_scrollok(win);
3423 }
3424
3425 static void
3426 newwin_legend(FRAME * curp)
3427 {
3428     static const struct {
3429         const char *msg;
3430         int code;
3431     } legend[] = {
3432         {
3433             "^C = create window", 0
3434         },
3435         {
3436             "^N = next window", 0
3437         },
3438         {
3439             "^P = previous window", 0
3440         },
3441         {
3442             "^F = scroll forward", 0
3443         },
3444         {
3445             "^B = scroll backward", 0
3446         },
3447         {
3448             "^K = keypad(%s)", 1
3449         },
3450         {
3451             "^S = scrollok(%s)", 2
3452         },
3453         {
3454             "^W = save window to file", 0
3455         },
3456         {
3457             "^R = restore window", 0
3458         },
3459 #if HAVE_WRESIZE
3460         {
3461             "^X = resize", 0
3462         },
3463 #endif
3464         {
3465             "^Q%s = exit", 3
3466         }
3467     };
3468     size_t n;
3469     int x;
3470     bool do_keypad = HaveKeypad(curp);
3471     bool do_scroll = HaveScroll(curp);
3472     char buf[BUFSIZ];
3473
3474     move(LINES - 4, 0);
3475     for (n = 0; n < SIZEOF(legend); n++) {
3476         switch (legend[n].code) {
3477         default:
3478             strcpy(buf, legend[n].msg);
3479             break;
3480         case 1:
3481             sprintf(buf, legend[n].msg, do_keypad ? "yes" : "no");
3482             break;
3483         case 2:
3484             sprintf(buf, legend[n].msg, do_scroll ? "yes" : "no");
3485             break;
3486         case 3:
3487             sprintf(buf, legend[n].msg, do_keypad ? "/ESC" : "");
3488             break;
3489         }
3490         x = getcurx(stdscr);
3491         addstr((COLS < (x + 3 + (int) strlen(buf))) ? "\n" : (n ? ", " : ""));
3492         addstr(buf);
3493     }
3494     clrtoeol();
3495 }
3496
3497 static void
3498 transient(FRAME * curp, NCURSES_CONST char *msg)
3499 {
3500     newwin_legend(curp);
3501     if (msg) {
3502         mvaddstr(LINES - 1, 0, msg);
3503         refresh();
3504         napms(1000);
3505     }
3506
3507     move(LINES - 1, 0);
3508     printw("%s characters are echoed, window should %sscroll.",
3509            HaveKeypad(curp) ? "Non-arrow" : "All other",
3510            HaveScroll(curp) ? "" : "not ");
3511     clrtoeol();
3512 }
3513
3514 static void
3515 newwin_report(FRAME * curp)
3516 /* report on the cursor's current position, then restore it */
3517 {
3518     WINDOW *win = (curp != 0) ? curp->wind : stdscr;
3519     int y, x;
3520
3521     if (win != stdscr)
3522         transient(curp, (char *) 0);
3523     getyx(win, y, x);
3524     move(LINES - 1, COLS - 17);
3525     printw("Y = %2d X = %2d", y, x);
3526     if (win != stdscr)
3527         refresh();
3528     else
3529         wmove(win, y, x);
3530 }
3531
3532 static pair *
3533 selectcell(int uli, int ulj, int lri, int lrj)
3534 /* arrows keys move cursor, return location at current on non-arrow key */
3535 {
3536     static pair res;            /* result cell */
3537     int si = lri - uli + 1;     /* depth of the select area */
3538     int sj = lrj - ulj + 1;     /* width of the select area */
3539     int i = 0, j = 0;           /* offsets into the select area */
3540
3541     res.y = uli;
3542     res.x = ulj;
3543     for (;;) {
3544         move(uli + i, ulj + j);
3545         newwin_report((FRAME *) 0);
3546
3547         switch (Getchar()) {
3548         case KEY_UP:
3549             i += si - 1;
3550             break;
3551         case KEY_DOWN:
3552             i++;
3553             break;
3554         case KEY_LEFT:
3555             j += sj - 1;
3556             break;
3557         case KEY_RIGHT:
3558             j++;
3559             break;
3560           case_QUIT:
3561             return ((pair *) 0);
3562 #ifdef NCURSES_MOUSE_VERSION
3563         case KEY_MOUSE:
3564             {
3565                 MEVENT event;
3566
3567                 getmouse(&event);
3568                 if (event.y > uli && event.x > ulj) {
3569                     i = event.y - uli;
3570                     j = event.x - ulj;
3571                 } else {
3572                     beep();
3573                     break;
3574                 }
3575             }
3576             /* FALLTHRU */
3577 #endif
3578         default:
3579             res.y = uli + i;
3580             res.x = ulj + j;
3581             return (&res);
3582         }
3583         i %= si;
3584         j %= sj;
3585     }
3586 }
3587
3588 static void
3589 outerbox(pair ul, pair lr, bool onoff)
3590 /* draw or erase a box *outside* the given pair of corners */
3591 {
3592     mvaddch(ul.y - 1, lr.x - 1, onoff ? ACS_ULCORNER : ' ');
3593     mvaddch(ul.y - 1, lr.x + 1, onoff ? ACS_URCORNER : ' ');
3594     mvaddch(lr.y + 1, lr.x + 1, onoff ? ACS_LRCORNER : ' ');
3595     mvaddch(lr.y + 1, ul.x - 1, onoff ? ACS_LLCORNER : ' ');
3596     move(ul.y - 1, ul.x);
3597     hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
3598     move(ul.y, ul.x - 1);
3599     vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
3600     move(lr.y + 1, ul.x);
3601     hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
3602     move(ul.y, lr.x + 1);
3603     vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
3604 }
3605
3606 static WINDOW *
3607 getwindow(void)
3608 /* Ask user for a window definition */
3609 {
3610     WINDOW *rwindow;
3611     pair ul, lr, *tmp;
3612
3613     move(0, 0);
3614     clrtoeol();
3615     addstr("Use arrows to move cursor, anything else to mark corner 1");
3616     refresh();
3617     if ((tmp = selectcell(2, 1, LINES - BOTLINES - 2, COLS - 2)) == (pair *) 0)
3618         return ((WINDOW *) 0);
3619     memcpy(&ul, tmp, sizeof(pair));
3620     mvaddch(ul.y - 1, ul.x - 1, ACS_ULCORNER);
3621     move(0, 0);
3622     clrtoeol();
3623     addstr("Use arrows to move cursor, anything else to mark corner 2");
3624     refresh();
3625     if ((tmp = selectcell(ul.y, ul.x, LINES - BOTLINES - 2, COLS - 2)) ==
3626         (pair *) 0)
3627         return ((WINDOW *) 0);
3628     memcpy(&lr, tmp, sizeof(pair));
3629
3630     rwindow = subwin(stdscr, lr.y - ul.y + 1, lr.x - ul.x + 1, ul.y, ul.x);
3631
3632     outerbox(ul, lr, TRUE);
3633     refresh();
3634
3635     wrefresh(rwindow);
3636
3637     move(0, 0);
3638     clrtoeol();
3639     return (rwindow);
3640 }
3641
3642 static void
3643 newwin_move(FRAME * curp, int dy, int dx)
3644 {
3645     WINDOW *win = (curp != 0) ? curp->wind : stdscr;
3646     int cur_y, cur_x;
3647     int max_y, max_x;
3648
3649     getyx(win, cur_y, cur_x);
3650     getmaxyx(win, max_y, max_x);
3651     if ((cur_x += dx) < 0)
3652         cur_x = 0;
3653     else if (cur_x >= max_x)
3654         cur_x = max_x - 1;
3655     if ((cur_y += dy) < 0)
3656         cur_y = 0;
3657     else if (cur_y >= max_y)
3658         cur_y = max_y - 1;
3659     wmove(win, cur_y, cur_x);
3660 }
3661
3662 static FRAME *
3663 delete_framed(FRAME * fp, bool showit)
3664 {
3665     FRAME *np;
3666
3667     fp->last->next = fp->next;
3668     fp->next->last = fp->last;
3669
3670     if (showit) {
3671         werase(fp->wind);
3672         wrefresh(fp->wind);
3673     }
3674     delwin(fp->wind);
3675
3676     np = (fp == fp->next) ? 0 : fp->next;
3677     free(fp);
3678     return np;
3679 }
3680
3681 static void
3682 acs_and_scroll(void)
3683 /* Demonstrate windows */
3684 {
3685     int c, i;
3686     FRAME *current = (FRAME *) 0, *neww;
3687     WINDOW *usescr = stdscr;
3688 #if HAVE_PUTWIN && HAVE_GETWIN
3689     FILE *fp;
3690 #endif
3691
3692 #define DUMPFILE        "screendump"
3693
3694 #ifdef NCURSES_MOUSE_VERSION
3695     mousemask(BUTTON1_CLICKED, (mmask_t *) 0);
3696 #endif
3697     c = CTRL('C');
3698     raw();
3699     do {
3700         transient((FRAME *) 0, (char *) 0);
3701         switch (c) {
3702         case CTRL('C'):
3703             neww = (FRAME *) calloc(1, sizeof(FRAME));
3704             if ((neww->wind = getwindow()) == (WINDOW *) 0)
3705                 goto breakout;
3706
3707             if (current == 0) { /* First element,  */
3708                 neww->next = neww;      /*   so point it at itself */
3709                 neww->last = neww;
3710             } else {
3711                 neww->next = current->next;
3712                 neww->last = current;
3713                 neww->last->next = neww;
3714                 neww->next->last = neww;
3715             }
3716             current = neww;
3717             /* SVr4 curses sets the keypad on all newly-created windows to
3718              * false.  Someone reported that PDCurses makes new windows inherit
3719              * this flag.  Remove the following 'keypad()' call to test this
3720              */
3721             keypad(current->wind, TRUE);
3722             current->do_keypad = HaveKeypad(current);
3723             current->do_scroll = HaveScroll(current);
3724             break;
3725
3726         case CTRL('N'): /* go to next window */
3727             if (current)
3728                 current = current->next;
3729             break;
3730
3731         case CTRL('P'): /* go to previous window */
3732             if (current)
3733                 current = current->last;
3734             break;
3735
3736         case CTRL('F'): /* scroll current window forward */
3737             if (current)
3738                 wscrl(current->wind, 1);
3739             break;
3740
3741         case CTRL('B'): /* scroll current window backwards */
3742             if (current)
3743                 wscrl(current->wind, -1);
3744             break;
3745
3746         case CTRL('K'): /* toggle keypad mode for current */
3747             if (current) {
3748                 current->do_keypad = !current->do_keypad;
3749                 keypad(current->wind, current->do_keypad);
3750             }
3751             break;
3752
3753         case CTRL('S'):
3754             if (current) {
3755                 current->do_scroll = !current->do_scroll;
3756                 scrollok(current->wind, current->do_scroll);
3757             }
3758             break;
3759
3760 #if HAVE_PUTWIN && HAVE_GETWIN
3761         case CTRL('W'): /* save and delete window */
3762             if (current == current->next) {
3763                 transient(current, "Will not save/delete ONLY window");
3764                 break;
3765             } else if ((fp = fopen(DUMPFILE, "w")) == (FILE *) 0) {
3766                 transient(current, "Can't open screen dump file");
3767             } else {
3768                 (void) putwin(current->wind, fp);
3769                 (void) fclose(fp);
3770
3771                 current = delete_framed(current, TRUE);
3772             }
3773             break;
3774
3775         case CTRL('R'): /* restore window */
3776             if ((fp = fopen(DUMPFILE, "r")) == (FILE *) 0) {
3777                 transient(current, "Can't open screen dump file");
3778             } else {
3779                 neww = (FRAME *) calloc(1, sizeof(FRAME));
3780
3781                 neww->next = current->next;
3782                 neww->last = current;
3783                 neww->last->next = neww;
3784                 neww->next->last = neww;
3785
3786                 neww->wind = getwin(fp);
3787                 (void) fclose(fp);
3788
3789                 wrefresh(neww->wind);
3790             }
3791             break;
3792 #endif
3793
3794 #if HAVE_WRESIZE
3795         case CTRL('X'): /* resize window */
3796             if (current) {
3797                 pair *tmp, ul, lr;
3798                 int mx, my;
3799
3800                 move(0, 0);
3801                 clrtoeol();
3802                 addstr("Use arrows to move cursor, anything else to mark new corner");
3803                 refresh();
3804
3805                 getbegyx(current->wind, ul.y, ul.x);
3806
3807                 tmp = selectcell(ul.y, ul.x, LINES - BOTLINES - 2, COLS - 2);
3808                 if (tmp == (pair *) 0) {
3809                     beep();
3810                     break;
3811                 }
3812
3813                 getmaxyx(current->wind, lr.y, lr.x);
3814                 lr.y += (ul.y - 1);
3815                 lr.x += (ul.x - 1);
3816                 outerbox(ul, lr, FALSE);
3817                 wnoutrefresh(stdscr);
3818
3819                 /* strictly cosmetic hack for the test */
3820                 getmaxyx(current->wind, my, mx);
3821                 if (my > tmp->y - ul.y) {
3822                     getyx(current->wind, lr.y, lr.x);
3823                     wmove(current->wind, tmp->y - ul.y + 1, 0);
3824                     wclrtobot(current->wind);
3825                     wmove(current->wind, lr.y, lr.x);
3826                 }
3827                 if (mx > tmp->x - ul.x)
3828                     for (i = 0; i < my; i++) {
3829                         wmove(current->wind, i, tmp->x - ul.x + 1);
3830                         wclrtoeol(current->wind);
3831                     }
3832                 wnoutrefresh(current->wind);
3833
3834                 memcpy(&lr, tmp, sizeof(pair));
3835                 (void) wresize(current->wind, lr.y - ul.y + 0, lr.x - ul.x + 0);
3836
3837                 getbegyx(current->wind, ul.y, ul.x);
3838                 getmaxyx(current->wind, lr.y, lr.x);
3839                 lr.y += (ul.y - 1);
3840                 lr.x += (ul.x - 1);
3841                 outerbox(ul, lr, TRUE);
3842                 wnoutrefresh(stdscr);
3843
3844                 wnoutrefresh(current->wind);
3845                 move(0, 0);
3846                 clrtoeol();
3847                 doupdate();
3848             }
3849             break;
3850 #endif /* HAVE_WRESIZE */
3851
3852         case KEY_F(10): /* undocumented --- use this to test area clears */
3853             selectcell(0, 0, LINES - 1, COLS - 1);
3854             clrtobot();
3855             refresh();
3856             break;
3857
3858         case KEY_UP:
3859             newwin_move(current, -1, 0);
3860             break;
3861         case KEY_DOWN:
3862             newwin_move(current, 1, 0);
3863             break;
3864         case KEY_LEFT:
3865             newwin_move(current, 0, -1);
3866             break;
3867         case KEY_RIGHT:
3868             newwin_move(current, 0, 1);
3869             break;
3870
3871         case KEY_BACKSPACE:
3872             /* FALLTHROUGH */
3873         case KEY_DC:
3874             {
3875                 int y, x;
3876                 getyx(current->wind, y, x);
3877                 if (--x < 0) {
3878                     if (--y < 0)
3879                         break;
3880                     x = getmaxx(current->wind) - 1;
3881                 }
3882                 mvwdelch(current->wind, y, x);
3883             }
3884             break;
3885
3886         case '\r':
3887             c = '\n';
3888             /* FALLTHROUGH */
3889
3890         default:
3891             if (current)
3892                 waddch(current->wind, (chtype) c);
3893             else
3894                 beep();
3895             break;
3896         }
3897         newwin_report(current);
3898         usescr = (current ? current->wind : stdscr);
3899         wrefresh(usescr);
3900     } while
3901         (!isQuit(c = wGetchar(usescr))
3902          && (c != ERR));
3903
3904   breakout:
3905     while (current != 0)
3906         current = delete_framed(current, FALSE);
3907
3908     scrollok(stdscr, TRUE);     /* reset to driver's default */
3909 #ifdef NCURSES_MOUSE_VERSION
3910     mousemask(0, (mmask_t *) 0);
3911 #endif
3912     noraw();
3913     erase();
3914     endwin();
3915 }
3916
3917 /****************************************************************************
3918  *
3919  * Panels tester
3920  *
3921  ****************************************************************************/
3922
3923 #if USE_LIBPANEL
3924 static int nap_msec = 1;
3925
3926 static NCURSES_CONST char *mod[] =
3927 {
3928     "test ",
3929     "TEST ",
3930     "(**) ",
3931     "*()* ",
3932     "<--> ",
3933     "LAST "
3934 };
3935
3936 /*+-------------------------------------------------------------------------
3937         wait_a_while(msec)
3938 --------------------------------------------------------------------------*/
3939 static void
3940 wait_a_while(int msec GCC_UNUSED)
3941 {
3942 #if HAVE_NAPMS
3943     if (nap_msec == 1)
3944         wGetchar(stdscr);
3945     else
3946         napms(nap_msec);
3947 #else
3948     if (nap_msec == 1)
3949         wGetchar(stdscr);
3950     else if (msec > 1000)
3951         sleep((unsigned) msec / 1000);
3952     else
3953         sleep(1);
3954 #endif
3955 }                               /* end of wait_a_while */
3956
3957 /*+-------------------------------------------------------------------------
3958         saywhat(text)
3959 --------------------------------------------------------------------------*/
3960 static void
3961 saywhat(NCURSES_CONST char *text)
3962 {
3963     wmove(stdscr, LINES - 1, 0);
3964     wclrtoeol(stdscr);
3965     if (text != 0 && *text != '\0') {
3966         waddstr(stdscr, text);
3967         waddstr(stdscr, "; ");
3968     }
3969     waddstr(stdscr, "press any key to continue");
3970 }                               /* end of saywhat */
3971
3972 /*+-------------------------------------------------------------------------
3973         mkpanel(rows,cols,tly,tlx) - alloc a win and panel and associate them
3974 --------------------------------------------------------------------------*/
3975 static PANEL *
3976 mkpanel(short color, int rows, int cols, int tly, int tlx)
3977 {
3978     WINDOW *win;
3979     PANEL *pan = 0;
3980
3981     if ((win = newwin(rows, cols, tly, tlx)) != 0) {
3982         if ((pan = new_panel(win)) == 0) {
3983             delwin(win);
3984         } else if (use_colors) {
3985             short fg = (color == COLOR_BLUE) ? COLOR_WHITE : COLOR_BLACK;
3986             short bg = color;
3987
3988             init_pair(color, fg, bg);
3989             wbkgdset(win, (chtype) (COLOR_PAIR(color) | ' '));
3990         } else {
3991             wbkgdset(win, A_BOLD | ' ');
3992         }
3993     }
3994     return pan;
3995 }                               /* end of mkpanel */
3996
3997 /*+-------------------------------------------------------------------------
3998         rmpanel(pan)
3999 --------------------------------------------------------------------------*/
4000 static void
4001 rmpanel(PANEL * pan)
4002 {
4003     WINDOW *win = panel_window(pan);
4004     del_panel(pan);
4005     delwin(win);
4006 }                               /* end of rmpanel */
4007
4008 /*+-------------------------------------------------------------------------
4009         pflush()
4010 --------------------------------------------------------------------------*/
4011 static void
4012 pflush(void)
4013 {
4014     update_panels();
4015     doupdate();
4016 }                               /* end of pflush */
4017
4018 /*+-------------------------------------------------------------------------
4019         fill_panel(win)
4020 --------------------------------------------------------------------------*/
4021 static void
4022 init_panel(void)
4023 {
4024     register int y, x;
4025
4026     for (y = 0; y < LINES - 1; y++) {
4027         for (x = 0; x < COLS; x++)
4028             wprintw(stdscr, "%d", (y + x) % 10);
4029     }
4030 }
4031
4032 static void
4033 fill_panel(PANEL * pan)
4034 {
4035     WINDOW *win = panel_window(pan);
4036     int num = ((const char *) panel_userptr(pan))[1];
4037     int y, x;
4038
4039     wmove(win, 1, 1);
4040     wprintw(win, "-pan%c-", num);
4041     wclrtoeol(win);
4042     box(win, 0, 0);
4043     for (y = 2; y < getmaxy(win) - 1; y++) {
4044         for (x = 1; x < getmaxx(win) - 1; x++) {
4045             wmove(win, y, x);
4046             waddch(win, UChar(num));
4047         }
4048     }
4049 }
4050
4051 #if USE_WIDEC_SUPPORT
4052 static void
4053 make_fullwidth_digit(cchar_t *target, int digit)
4054 {
4055     wchar_t source[2];
4056
4057     source[0] = digit + 0xff10;
4058     source[1] = 0;
4059     setcchar(target, source, A_NORMAL, 0, 0);
4060 }
4061
4062 static void
4063 init_wide_panel(void)
4064 {
4065     int digit;
4066     cchar_t temp[10];
4067
4068     for (digit = 0; digit < 10; ++digit)
4069         make_fullwidth_digit(&temp[digit], digit);
4070
4071     do {
4072         int y, x;
4073         getyx(stdscr, y, x);
4074         digit = (y + x / 2) % 10;
4075     } while (add_wch(&temp[digit]) != ERR);
4076 }
4077
4078 static void
4079 fill_wide_panel(PANEL * pan)
4080 {
4081     WINDOW *win = panel_window(pan);
4082     int num = ((const char *) panel_userptr(pan))[1];
4083     int y, x;
4084
4085     wmove(win, 1, 1);
4086     wprintw(win, "-pan%c-", num);
4087     wclrtoeol(win);
4088     box(win, 0, 0);
4089     for (y = 2; y < getmaxy(win) - 1; y++) {
4090         for (x = 1; x < getmaxx(win) - 1; x++) {
4091             wmove(win, y, x);
4092             waddch(win, UChar(num));
4093         }
4094     }
4095 }
4096 #endif
4097
4098 #define MAX_PANELS 5
4099
4100 static void
4101 canned_panel(PANEL * px[MAX_PANELS + 1], NCURSES_CONST char *cmd)
4102 {
4103     int which = cmd[1] - '0';
4104
4105     saywhat(cmd);
4106     switch (*cmd) {
4107     case 'h':
4108         hide_panel(px[which]);
4109         break;
4110     case 's':
4111         show_panel(px[which]);
4112         break;
4113     case 't':
4114         top_panel(px[which]);
4115         break;
4116     case 'b':
4117         bottom_panel(px[which]);
4118         break;
4119     case 'd':
4120         rmpanel(px[which]);
4121         break;
4122     }
4123     pflush();
4124     wait_a_while(nap_msec);
4125 }
4126
4127 static void
4128 demo_panels(void (*InitPanel) (void), void (*FillPanel) (PANEL *))
4129 {
4130     int count;
4131     int itmp;
4132     PANEL *px[MAX_PANELS + 1];
4133
4134     scrollok(stdscr, FALSE);    /* we don't want stdscr to scroll! */
4135     refresh();
4136
4137     InitPanel();
4138     for (count = 0; count < 5; count++) {
4139         px[1] = mkpanel(COLOR_RED,
4140                         LINES / 2 - 2,
4141                         COLS / 8 + 1,
4142                         0,
4143                         0);
4144         set_panel_userptr(px[1], (NCURSES_CONST void *) "p1");
4145
4146         px[2] = mkpanel(COLOR_GREEN,
4147                         LINES / 2 + 1,
4148                         COLS / 7,
4149                         LINES / 4,
4150                         COLS / 10);
4151         set_panel_userptr(px[2], (NCURSES_CONST void *) "p2");
4152
4153         px[3] = mkpanel(COLOR_YELLOW,
4154                         LINES / 4,
4155                         COLS / 10,
4156                         LINES / 2,
4157                         COLS / 9);
4158         set_panel_userptr(px[3], (NCURSES_CONST void *) "p3");
4159
4160         px[4] = mkpanel(COLOR_BLUE,
4161                         LINES / 2 - 2,
4162                         COLS / 8,
4163                         LINES / 2 - 2,
4164                         COLS / 3);
4165         set_panel_userptr(px[4], (NCURSES_CONST void *) "p4");
4166
4167         px[5] = mkpanel(COLOR_MAGENTA,
4168                         LINES / 2 - 2,
4169                         COLS / 8,
4170                         LINES / 2,
4171                         COLS / 2 - 2);
4172         set_panel_userptr(px[5], (NCURSES_CONST void *) "p5");
4173
4174         FillPanel(px[1]);
4175         FillPanel(px[2]);
4176         FillPanel(px[3]);
4177         FillPanel(px[4]);
4178         FillPanel(px[5]);
4179
4180         hide_panel(px[4]);
4181         hide_panel(px[5]);
4182         pflush();
4183         saywhat("");
4184         wait_a_while(nap_msec);
4185
4186         saywhat("h3 s1 s2 s4 s5");
4187         move_panel(px[1], 0, 0);
4188         hide_panel(px[3]);
4189         show_panel(px[1]);
4190         show_panel(px[2]);
4191         show_panel(px[4]);
4192         show_panel(px[5]);
4193         pflush();
4194         wait_a_while(nap_msec);
4195
4196         canned_panel(px, "s1");
4197         canned_panel(px, "s2");
4198
4199         saywhat("m2");
4200         move_panel(px[2], LINES / 3 + 1, COLS / 8);
4201         pflush();
4202         wait_a_while(nap_msec);
4203
4204         canned_panel(px, "s3");
4205
4206         saywhat("m3");
4207         move_panel(px[3], LINES / 4 + 1, COLS / 15);
4208         pflush();
4209         wait_a_while(nap_msec);
4210
4211         canned_panel(px, "b3");
4212         canned_panel(px, "s4");
4213         canned_panel(px, "s5");
4214         canned_panel(px, "t3");
4215         canned_panel(px, "t1");
4216         canned_panel(px, "t2");
4217         canned_panel(px, "t3");
4218         canned_panel(px, "t4");
4219
4220         for (itmp = 0; itmp < 6; itmp++) {
4221             WINDOW *w4 = panel_window(px[4]);
4222             WINDOW *w5 = panel_window(px[5]);
4223
4224             saywhat("m4");
4225             wmove(w4, LINES / 8, 1);
4226             waddstr(w4, mod[itmp]);
4227             move_panel(px[4], LINES / 6, itmp * (COLS / 8));
4228             wmove(w5, LINES / 6, 1);
4229             waddstr(w5, mod[itmp]);
4230             pflush();
4231             wait_a_while(nap_msec);
4232
4233             saywhat("m5");
4234             wmove(w4, LINES / 6, 1);
4235             waddstr(w4, mod[itmp]);
4236             move_panel(px[5], LINES / 3 - 1, (itmp * 10) + 6);
4237             wmove(w5, LINES / 8, 1);
4238             waddstr(w5, mod[itmp]);
4239             pflush();
4240             wait_a_while(nap_msec);
4241         }
4242
4243         saywhat("m4");
4244         move_panel(px[4], LINES / 6, itmp * (COLS / 8));
4245         pflush();
4246         wait_a_while(nap_msec);
4247
4248         canned_panel(px, "t5");
4249         canned_panel(px, "t2");
4250         canned_panel(px, "t1");
4251         canned_panel(px, "d2");
4252         canned_panel(px, "h3");
4253         canned_panel(px, "d1");
4254         canned_panel(px, "d4");
4255         canned_panel(px, "d5");
4256         canned_panel(px, "d3");
4257
4258         wait_a_while(nap_msec);
4259         if (nap_msec == 1)
4260             break;
4261         nap_msec = 100L;
4262     }
4263
4264     erase();
4265     endwin();
4266 }
4267 #endif /* USE_LIBPANEL */
4268
4269 /****************************************************************************
4270  *
4271  * Pad tester
4272  *
4273  ****************************************************************************/
4274
4275 #define GRIDSIZE        3
4276
4277 static bool pending_pan = FALSE;
4278 static bool show_panner_legend = TRUE;
4279
4280 static int
4281 panner_legend(int line)
4282 {
4283     static const char *const legend[] =
4284     {
4285         "Use arrow keys (or U,D,L,R) to pan, ESC to quit, ! to shell-out.",
4286         "Use +,- (or j,k) to grow/shrink the panner vertically.",
4287         "Use <,> (or h,l) to grow/shrink the panner horizontally.",
4288         "Number repeats.  Toggle legend:? filler:a timer:t scrollmark:s."
4289     };
4290     int n = (SIZEOF(legend) - (LINES - line));
4291     if (line < LINES && (n >= 0)) {
4292         move(line, 0);
4293         if (show_panner_legend)
4294             printw("%s", legend[n]);
4295         clrtoeol();
4296         return show_panner_legend;
4297     }
4298     return FALSE;
4299 }
4300
4301 static void
4302 panner_h_cleanup(int from_y, int from_x, int to_x)
4303 {
4304     if (!panner_legend(from_y))
4305         do_h_line(from_y, from_x, ' ', to_x);
4306 }
4307
4308 static void
4309 panner_v_cleanup(int from_y, int from_x, int to_y)
4310 {
4311     if (!panner_legend(from_y))
4312         do_v_line(from_y, from_x, ' ', to_y);
4313 }
4314
4315 static void
4316 fill_pad(WINDOW *panpad, bool pan_lines)
4317 {
4318     int y, x;
4319     unsigned gridcount = 0;
4320
4321     wmove(panpad, 0, 0);
4322     for (y = 0; y < getmaxy(panpad); y++) {
4323         for (x = 0; x < getmaxx(panpad); x++) {
4324             if (y % GRIDSIZE == 0 && x % GRIDSIZE == 0) {
4325                 if (y == 0 && x == 0)
4326                     waddch(panpad, pan_lines ? ACS_ULCORNER : '+');
4327                 else if (y == 0)
4328                     waddch(panpad, pan_lines ? ACS_TTEE : '+');
4329                 else if (y == 0 || x == 0)
4330                     waddch(panpad, pan_lines ? ACS_LTEE : '+');
4331                 else
4332                     waddch(panpad, (chtype) ((pan_lines ? 'a' : 'A') +
4333                                              (gridcount++ % 26)));
4334             } else if (y % GRIDSIZE == 0)
4335                 waddch(panpad, pan_lines ? ACS_HLINE : '-');
4336             else if (x % GRIDSIZE == 0)
4337                 waddch(panpad, pan_lines ? ACS_VLINE : '|');
4338             else
4339                 waddch(panpad, ' ');
4340         }
4341     }
4342 }
4343
4344 static void
4345 panner(WINDOW *pad,
4346        int top_x, int top_y, int porty, int portx,
4347        int (*pgetc) (WINDOW *))
4348 {
4349 #if HAVE_GETTIMEOFDAY
4350     struct timeval before, after;
4351     bool timing = TRUE;
4352 #endif
4353     bool pan_lines = FALSE;
4354     bool scrollers = TRUE;
4355     int basex = 0;
4356     int basey = 0;
4357     int pxmax, pymax, lowend, highend, c;
4358
4359     getmaxyx(pad, pymax, pxmax);
4360     scrollok(stdscr, FALSE);    /* we don't want stdscr to scroll! */
4361
4362     c = KEY_REFRESH;
4363     do {
4364 #ifdef NCURSES_VERSION
4365         /*
4366          * During shell-out, the user may have resized the window.  Adjust
4367          * the port size of the pad to accommodate this.  Ncurses automatically
4368          * resizes all of the normal windows to fit on the new screen.
4369          */
4370         if (top_x > COLS)
4371             top_x = COLS;
4372         if (portx > COLS)
4373             portx = COLS;
4374         if (top_y > LINES)
4375             top_y = LINES;
4376         if (porty > LINES)
4377             porty = LINES;
4378 #endif
4379         switch (c) {
4380         case KEY_REFRESH:
4381             erase();
4382
4383             /* FALLTHRU */
4384         case '?':
4385             if (c == '?')
4386                 show_panner_legend = !show_panner_legend;
4387             panner_legend(LINES - 4);
4388             panner_legend(LINES - 3);
4389             panner_legend(LINES - 2);
4390             panner_legend(LINES - 1);
4391             break;
4392         case 'a':
4393             pan_lines = !pan_lines;
4394             fill_pad(pad, pan_lines);
4395             pending_pan = FALSE;
4396             break;
4397
4398 #if HAVE_GETTIMEOFDAY
4399         case 't':
4400             timing = !timing;
4401             if (!timing)
4402                 panner_legend(LINES - 1);
4403             break;
4404 #endif
4405         case 's':
4406             scrollers = !scrollers;
4407             break;
4408
4409             /* Move the top-left corner of the pad, keeping the bottom-right
4410              * corner fixed.
4411              */
4412         case 'h':               /* increase-columns: move left edge to left */
4413             if (top_x <= 0)
4414                 beep();
4415             else {
4416                 panner_v_cleanup(top_y, top_x, porty);
4417                 top_x--;
4418             }
4419             break;
4420
4421         case 'j':               /* decrease-lines: move top-edge down */
4422             if (top_y >= porty)
4423                 beep();
4424             else {
4425                 panner_h_cleanup(top_y - 1, top_x - (top_x > 0), portx);
4426                 top_y++;
4427             }
4428             break;
4429
4430         case 'k':               /* increase-lines: move top-edge up */
4431             if (top_y <= 0)
4432                 beep();
4433             else {
4434                 top_y--;
4435                 panner_h_cleanup(top_y, top_x, portx);
4436             }
4437             break;
4438
4439         case 'l':               /* decrease-columns: move left-edge to right */
4440             if (top_x >= portx)
4441                 beep();
4442             else {
4443                 panner_v_cleanup(top_y - (top_y > 0), top_x - 1, porty);
4444                 top_x++;
4445             }
4446             break;
4447
4448             /* Move the bottom-right corner of the pad, keeping the top-left
4449              * corner fixed.
4450              */
4451         case KEY_IC:            /* increase-columns: move right-edge to right */
4452             if (portx >= pxmax || portx >= COLS)
4453                 beep();
4454             else {
4455                 panner_v_cleanup(top_y - (top_y > 0), portx - 1, porty);
4456                 ++portx;
4457             }
4458             break;
4459
4460         case KEY_IL:            /* increase-lines: move bottom-edge down */
4461             if (porty >= pymax || porty >= LINES)
4462                 beep();
4463             else {
4464                 panner_h_cleanup(porty - 1, top_x - (top_x > 0), portx);
4465                 ++porty;
4466             }
4467             break;
4468
4469         case KEY_DC:            /* decrease-columns: move bottom edge up */
4470             if (portx <= top_x)
4471                 beep();
4472             else {
4473                 portx--;
4474                 panner_v_cleanup(top_y - (top_y > 0), portx, porty);
4475             }
4476             break;
4477
4478         case KEY_DL:            /* decrease-lines */
4479             if (porty <= top_y)
4480                 beep();
4481             else {
4482                 porty--;
4483                 panner_h_cleanup(porty, top_x - (top_x > 0), portx);
4484             }
4485             break;
4486
4487         case KEY_LEFT:          /* pan leftwards */
4488             if (basex > 0)
4489                 basex--;
4490             else
4491                 beep();
4492             break;
4493
4494         case KEY_RIGHT: /* pan rightwards */
4495             if (basex + portx - (pymax > porty) < pxmax)
4496                 basex++;
4497            &n