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