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