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