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