]> ncurses.scripts.mit.edu Git - ncurses.git/blob - test/ncurses.c
0e724a65df0bb51ad52ce834710eb1c13d952ee7
[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.302 2008/02/23 23:07:28 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 P(string)       printw("%s\n", string)
122
123 #define BLANK           ' '     /* this is the background character */
124
125 #undef max_colors
126 static int max_colors;          /* the actual number of colors we'll use */
127 static int min_colors;          /* the minimum color code */
128 static bool use_colors;         /* true if we use colors */
129
130 #undef max_pairs
131 static int max_pairs;           /* ...and the number of color pairs */
132
133 typedef struct {
134     short red;
135     short green;
136     short blue;
137 } RGB_DATA;
138
139 static RGB_DATA *all_colors;
140
141 static void main_menu(bool);
142
143 /* The behavior of mvhline, mvvline for negative/zero length is unspecified,
144  * though we can rely on negative x/y values to stop the macro.
145  */
146 static void
147 do_h_line(int y, int x, chtype c, int to)
148 {
149     if ((to) > (x))
150         mvhline(y, x, c, (to) - (x));
151 }
152
153 static void
154 do_v_line(int y, int x, chtype c, int to)
155 {
156     if ((to) > (y))
157         mvvline(y, x, c, (to) - (y));
158 }
159
160 static void
161 Repaint(void)
162 {
163     touchwin(stdscr);
164     touchwin(curscr);
165     wrefresh(curscr);
166 }
167
168 static bool
169 isQuit(int c)
170 {
171     return ((c) == QUIT || (c) == ESCAPE);
172 }
173 #define case_QUIT       case QUIT: case ESCAPE
174
175 /* Common function to allow ^T to toggle trace-mode in the middle of a test
176  * so that trace-files can be made smaller.
177  */
178 static int
179 wGetchar(WINDOW *win)
180 {
181     int c;
182 #ifdef TRACE
183     while ((c = wgetch(win)) == CTRL('T')) {
184         if (_nc_tracing) {
185             save_trace = _nc_tracing;
186             Trace(("TOGGLE-TRACING OFF"));
187             _nc_tracing = 0;
188         } else {
189             _nc_tracing = save_trace;
190         }
191         trace(_nc_tracing);
192         if (_nc_tracing)
193             Trace(("TOGGLE-TRACING ON"));
194     }
195 #else
196     c = wgetch(win);
197 #endif
198     return c;
199 }
200 #define Getchar() wGetchar(stdscr)
201
202 /* replaces wgetnstr(), since we want to be able to edit values */
203 static void
204 wGetstring(WINDOW *win, char *buffer, int limit)
205 {
206     int y0, x0, x, ch;
207     bool done = FALSE;
208
209     echo();
210     getyx(win, y0, x0);
211     wattrset(win, A_REVERSE);
212
213     x = strlen(buffer);
214     while (!done) {
215         if (x > (int) strlen(buffer))
216             x = (int) strlen(buffer);
217         wmove(win, y0, x0);
218         wprintw(win, "%-*s", limit, buffer);
219         wmove(win, y0, x0 + x);
220         switch (ch = wGetchar(win)) {
221         case '\n':
222         case KEY_ENTER:
223             done = TRUE;
224             break;
225         case CTRL('U'):
226             *buffer = '\0';
227             break;
228         case '\b':
229         case KEY_BACKSPACE:
230         case KEY_DC:
231             if (x > 0) {
232                 int j;
233                 for (j = --x; (buffer[j] = buffer[j + 1]) != '\0'; ++j) {
234                     ;
235                 }
236             } else {
237                 beep();
238             }
239             break;
240         case KEY_LEFT:
241             if (x > 0) {
242                 --x;
243             } else {
244                 flash();
245             }
246             break;
247         case KEY_RIGHT:
248             ++x;
249             break;
250         default:
251             if (!isprint(ch) || ch >= KEY_MIN) {
252                 beep();
253             } else if ((int) strlen(buffer) < limit) {
254                 int j;
255                 for (j = strlen(buffer) + 1; j > x; --j) {
256                     buffer[j] = buffer[j - 1];
257                 }
258                 buffer[x++] = ch;
259             } else {
260                 flash();
261             }
262         }
263     }
264
265     wattroff(win, A_REVERSE);
266     wmove(win, y0, x0);
267     noecho();
268 }
269
270 #if USE_WIDEC_SUPPORT
271 static int
272 wGet_wchar(WINDOW *win, wint_t *result)
273 {
274     int c;
275 #ifdef TRACE
276     while ((c = wget_wch(win, result)) == CTRL('T')) {
277         if (_nc_tracing) {
278             save_trace = _nc_tracing;
279             Trace(("TOGGLE-TRACING OFF"));
280             _nc_tracing = 0;
281         } else {
282             _nc_tracing = save_trace;
283         }
284         trace(_nc_tracing);
285         if (_nc_tracing)
286             Trace(("TOGGLE-TRACING ON"));
287     }
288 #else
289     c = wget_wch(win, result);
290 #endif
291     return c;
292 }
293 #define Get_wchar(result) wGet_wchar(stdscr, result)
294
295 /* replaces wgetn_wstr(), since we want to be able to edit values */
296 static void
297 wGet_wstring(WINDOW *win, wchar_t *buffer, int limit)
298 {
299     int y0, x0, x;
300     wint_t ch;
301     bool done = FALSE;
302     bool fkey = FALSE;
303
304     echo();
305     getyx(win, y0, x0);
306     wattrset(win, A_REVERSE);
307
308     x = wcslen(buffer);
309     while (!done) {
310         if (x > (int) wcslen(buffer))
311             x = (int) wcslen(buffer);
312
313         /* clear the "window' */
314         wmove(win, y0, x0);
315         wprintw(win, "%*s", limit, " ");
316
317         /* write the existing buffer contents */
318         wmove(win, y0, x0);
319         waddnwstr(win, buffer, limit);
320
321         /* positions the cursor past character 'x' */
322         wmove(win, y0, x0);
323         waddnwstr(win, buffer, x);
324
325         switch (wGet_wchar(win, &ch)) {
326         case KEY_CODE_YES:
327             fkey = TRUE;
328             switch (ch) {
329             case KEY_ENTER:
330                 ch = '\n';
331                 fkey = FALSE;
332                 break;
333             case KEY_BACKSPACE:
334             case KEY_DC:
335                 ch = '\b';
336                 fkey = FALSE;
337                 break;
338             case KEY_LEFT:
339             case KEY_RIGHT:
340                 break;
341             default:
342                 ch = (wint_t) -1;
343                 break;
344             }
345             break;
346         case OK:
347             fkey = FALSE;
348             break;
349         default:
350             ch = (wint_t) -1;
351             fkey = TRUE;
352             break;
353         }
354
355         switch (ch) {
356         case '\n':
357             done = TRUE;
358             break;
359         case CTRL('U'):
360             *buffer = '\0';
361             break;
362         case '\b':
363             if (x > 0) {
364                 int j;
365                 for (j = --x; (buffer[j] = buffer[j + 1]) != '\0'; ++j) {
366                     ;
367                 }
368             } else {
369                 beep();
370             }
371             break;
372         case KEY_LEFT:
373             if (x > 0) {
374                 --x;
375             } else {
376                 beep();
377             }
378             break;
379         case KEY_RIGHT:
380             ++x;
381             break;
382         default:
383             if (fkey) {
384                 beep();
385             } else if ((int) wcslen(buffer) < limit) {
386                 int j;
387                 for (j = wcslen(buffer) + 1; j > x; --j) {
388                     buffer[j] = buffer[j - 1];
389                 }
390                 buffer[x++] = ch;
391             } else {
392                 beep();
393             }
394         }
395     }
396
397     wattroff(win, A_REVERSE);
398     wmove(win, y0, x0);
399     noecho();
400 }
401
402 #endif
403
404 static void
405 Pause(void)
406 {
407     move(LINES - 1, 0);
408     addstr("Press any key to continue... ");
409     (void) Getchar();
410 }
411
412 static void
413 Cannot(const char *what)
414 {
415     printw("\nThis %s terminal %s\n\n", getenv("TERM"), what);
416     Pause();
417 }
418
419 static void
420 ShellOut(bool message)
421 {
422     if (message)
423         addstr("Shelling out...");
424     def_prog_mode();
425     endwin();
426     system("sh");
427     if (message)
428         addstr("returned from shellout.\n");
429     refresh();
430 }
431
432 #ifdef NCURSES_MOUSE_VERSION
433 /*
434  * This function is the same as _tracemouse(), but we cannot count on that
435  * being available in the non-debug library.
436  */
437 static const char *
438 mouse_decode(MEVENT const *ep)
439 {
440     static char buf[80 + (5 * 10) + (32 * 15)];
441
442     (void) sprintf(buf, "id %2d  at (%2d, %2d, %2d) state %4lx = {",
443                    ep->id, ep->x, ep->y, ep->z, (unsigned long) ep->bstate);
444
445 #define SHOW(m, s) if ((ep->bstate & m)==m) {strcat(buf,s); strcat(buf, ", ");}
446
447     SHOW(BUTTON1_RELEASED, "release-1");
448     SHOW(BUTTON1_PRESSED, "press-1");
449     SHOW(BUTTON1_CLICKED, "click-1");
450     SHOW(BUTTON1_DOUBLE_CLICKED, "doubleclick-1");
451     SHOW(BUTTON1_TRIPLE_CLICKED, "tripleclick-1");
452 #if NCURSES_MOUSE_VERSION == 1
453     SHOW(BUTTON1_RESERVED_EVENT, "reserved-1");
454 #endif
455
456     SHOW(BUTTON2_RELEASED, "release-2");
457     SHOW(BUTTON2_PRESSED, "press-2");
458     SHOW(BUTTON2_CLICKED, "click-2");
459     SHOW(BUTTON2_DOUBLE_CLICKED, "doubleclick-2");
460     SHOW(BUTTON2_TRIPLE_CLICKED, "tripleclick-2");
461 #if NCURSES_MOUSE_VERSION == 1
462     SHOW(BUTTON2_RESERVED_EVENT, "reserved-2");
463 #endif
464
465     SHOW(BUTTON3_RELEASED, "release-3");
466     SHOW(BUTTON3_PRESSED, "press-3");
467     SHOW(BUTTON3_CLICKED, "click-3");
468     SHOW(BUTTON3_DOUBLE_CLICKED, "doubleclick-3");
469     SHOW(BUTTON3_TRIPLE_CLICKED, "tripleclick-3");
470 #if NCURSES_MOUSE_VERSION == 1
471     SHOW(BUTTON3_RESERVED_EVENT, "reserved-3");
472 #endif
473
474     SHOW(BUTTON4_RELEASED, "release-4");
475     SHOW(BUTTON4_PRESSED, "press-4");
476     SHOW(BUTTON4_CLICKED, "click-4");
477     SHOW(BUTTON4_DOUBLE_CLICKED, "doubleclick-4");
478     SHOW(BUTTON4_TRIPLE_CLICKED, "tripleclick-4");
479 #if NCURSES_MOUSE_VERSION == 1
480     SHOW(BUTTON4_RESERVED_EVENT, "reserved-4");
481 #endif
482
483 #if NCURSES_MOUSE_VERSION == 2
484     SHOW(BUTTON5_RELEASED, "release-5");
485     SHOW(BUTTON5_PRESSED, "press-5");
486     SHOW(BUTTON5_CLICKED, "click-5");
487     SHOW(BUTTON5_DOUBLE_CLICKED, "doubleclick-5");
488     SHOW(BUTTON5_TRIPLE_CLICKED, "tripleclick-5");
489 #endif
490
491     SHOW(BUTTON_CTRL, "ctrl");
492     SHOW(BUTTON_SHIFT, "shift");
493     SHOW(BUTTON_ALT, "alt");
494     SHOW(ALL_MOUSE_EVENTS, "all-events");
495     SHOW(REPORT_MOUSE_POSITION, "position");
496
497 #undef SHOW
498
499     if (buf[strlen(buf) - 1] == ' ')
500         buf[strlen(buf) - 2] = '\0';
501     (void) strcat(buf, "}");
502     return (buf);
503 }
504 #endif /* NCURSES_MOUSE_VERSION */
505
506 /****************************************************************************
507  *
508  * Character input test
509  *
510  ****************************************************************************/
511
512 static void
513 setup_getch(WINDOW *win, bool flags[])
514 {
515     keypad(win, flags['k']);    /* should be redundant, but for testing */
516     meta(win, flags['m']);      /* force this to a known state */
517     if (flags['e'])
518         echo();
519     else
520         noecho();
521 }
522
523 static void
524 wgetch_help(WINDOW *win, bool flags[])
525 {
526     static const char *help[] =
527     {
528         "e  -- toggle echo mode"
529         ,"g  -- triggers a getstr test"
530         ,"k  -- toggle keypad/literal mode"
531         ,"m  -- toggle meta (7-bit/8-bit) mode"
532         ,"^q -- quit"
533         ,"s  -- shell out\n"
534         ,"w  -- create a new window"
535 #ifdef SIGTSTP
536         ,"z  -- suspend this process"
537 #endif
538     };
539     int y, x;
540     unsigned chk = ((SIZEOF(help) + 1) / 2);
541     unsigned n;
542
543     getyx(win, y, x);
544     move(0, 0);
545     printw("Type any key to see its %s value.  Also:\n",
546            flags['k'] ? "keypad" : "literal");
547     for (n = 0; n < SIZEOF(help); ++n) {
548         int row = 1 + (n % chk);
549         int col = (n >= chk) ? COLS / 2 : 0;
550         int flg = ((strstr(help[n], "toggle") != 0)
551                    && (flags[UChar(*help[n])] != FALSE));
552         if (flg)
553             standout();
554         mvprintw(row, col, "%s", help[n]);
555         if (col == 0)
556             clrtoeol();
557         if (flg)
558             standend();
559     }
560     wrefresh(stdscr);
561     wmove(win, y, x);
562 }
563
564 static void
565 wgetch_wrap(WINDOW *win, int first_y)
566 {
567     int last_y = getmaxy(win) - 1;
568     int y = getcury(win) + 1;
569
570     if (y >= last_y)
571         y = first_y;
572     wmove(win, y, 0);
573     wclrtoeol(win);
574 }
575
576 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
577 typedef struct {
578     WINDOW *text;
579     WINDOW *frame;
580 } WINSTACK;
581
582 static WINSTACK *winstack = 0;
583 static unsigned len_winstack = 0;
584
585 static void
586 forget_boxes(void)
587 {
588     if (winstack != 0) {
589         free(winstack);
590     }
591     winstack = 0;
592     len_winstack = 0;
593 }
594
595 static void
596 remember_boxes(unsigned level, WINDOW *txt_win, WINDOW *box_win)
597 {
598     unsigned need = (level + 1) * 2;
599
600     if (winstack == 0) {
601         len_winstack = 20;
602         winstack = (WINSTACK *) malloc(len_winstack * sizeof(WINSTACK));
603     } else if (need >= len_winstack) {
604         len_winstack = need;
605         winstack = (WINSTACK *) realloc(winstack, len_winstack * sizeof(WINSTACK));
606     }
607     winstack[level].text = txt_win;
608     winstack[level].frame = box_win;
609 }
610
611 #if USE_SOFTKEYS && (NCURSES_VERSION_PATCH < 20071229) && NCURSES_EXT_FUNCS
612 static void
613 slk_repaint(void)
614 {
615     /* this chunk is now done in resize_term() */
616     slk_touch();
617     slk_clear();
618     slk_noutrefresh();
619 }
620
621 #else
622 #define slk_repaint()           /* nothing */
623 #endif
624
625 /*
626  * For wgetch_test(), we create pairs of windows - one for a box, one for text.
627  * Resize both and paint the box in the parent.
628  */
629 static void
630 resize_boxes(unsigned level, WINDOW *win)
631 {
632     unsigned n;
633     int base = 5;
634     int high = LINES - base;
635     int wide = COLS;
636
637     touchwin(stdscr);
638     wnoutrefresh(stdscr);
639
640     slk_repaint();
641
642     for (n = 0; n < level; ++n) {
643         wresize(winstack[n].frame, high, wide);
644         wresize(winstack[n].text, high - 2, wide - 2);
645         high -= 2;
646         wide -= 2;
647         werase(winstack[n].text);
648         box(winstack[n].frame, 0, 0);
649         wnoutrefresh(winstack[n].frame);
650         wprintw(winstack[n].text,
651                 "size %dx%d\n",
652                 getmaxy(winstack[n].text),
653                 getmaxx(winstack[n].text));
654         wnoutrefresh(winstack[n].text);
655         if (winstack[n].text == win)
656             break;
657     }
658     doupdate();
659 }
660 #else
661 #define forget_boxes()          /* nothing */
662 #define remember_boxes(level,text,frame)        /* nothing */
663 #endif
664
665 static void
666 wgetch_test(unsigned level, WINDOW *win, int delay)
667 {
668     char buf[BUFSIZ];
669     int first_y, first_x;
670     int c;
671     int incount = 0;
672     bool flags[256];
673     bool blocking = (delay < 0);
674
675     memset(flags, FALSE, sizeof(flags));
676     flags[UChar('k')] = (win == stdscr);
677
678     setup_getch(win, flags);
679     wtimeout(win, delay);
680     getyx(win, first_y, first_x);
681
682     wgetch_help(win, flags);
683     wsetscrreg(win, first_y, getmaxy(win) - 1);
684     scrollok(win, TRUE);
685
686     for (;;) {
687         while ((c = wGetchar(win)) == ERR) {
688             incount++;
689             if (blocking) {
690                 (void) wprintw(win, "%05d: input error", incount);
691                 break;
692             } else {
693                 (void) wprintw(win, "%05d: input timed out", incount);
694             }
695             wgetch_wrap(win, first_y);
696         }
697         if (c == ERR && blocking) {
698             wprintw(win, "ERR");
699             wgetch_wrap(win, first_y);
700         } else if (isQuit(c)) {
701             break;
702         } else if (c == 'e') {
703             flags[UChar('e')] = !flags[UChar('e')];
704             setup_getch(win, flags);
705             wgetch_help(win, flags);
706         } else if (c == 'g') {
707             waddstr(win, "getstr test: ");
708             echo();
709             wgetnstr(win, buf, sizeof(buf) - 1);
710             noecho();
711             wprintw(win, "I saw %d characters:\n\t`%s'.", (int) strlen(buf), buf);
712             wclrtoeol(win);
713             wgetch_wrap(win, first_y);
714         } else if (c == 'k') {
715             flags[UChar('k')] = !flags[UChar('k')];
716             setup_getch(win, flags);
717             wgetch_help(win, flags);
718         } else if (c == 'm') {
719             flags[UChar('m')] = !flags[UChar('m')];
720             setup_getch(win, flags);
721             wgetch_help(win, flags);
722         } else if (c == 's') {
723             ShellOut(TRUE);
724         } else if (c == 'w') {
725             int high = getmaxy(win) - 1 - first_y + 1;
726             int wide = getmaxx(win) - first_x;
727             int old_y, old_x;
728             int new_y = first_y + getbegy(win);
729             int new_x = first_x + getbegx(win);
730
731             getyx(win, old_y, old_x);
732             if (high > 2 && wide > 2) {
733                 WINDOW *wb = newwin(high, wide, new_y, new_x);
734                 WINDOW *wi = newwin(high - 2, wide - 2, new_y + 1, new_x + 1);
735
736                 box(wb, 0, 0);
737                 wrefresh(wb);
738                 wmove(wi, 0, 0);
739                 remember_boxes(level, wi, wb);
740                 wgetch_test(level + 1, wi, delay);
741                 delwin(wi);
742                 delwin(wb);
743
744                 wgetch_help(win, flags);
745                 wmove(win, old_y, old_x);
746                 touchwin(win);
747                 wrefresh(win);
748                 doupdate();
749             }
750 #ifdef SIGTSTP
751         } else if (c == 'z') {
752             kill(getpid(), SIGTSTP);
753 #endif
754         } else {
755             wprintw(win, "Key pressed: %04o ", c);
756 #ifdef NCURSES_MOUSE_VERSION
757             if (c == KEY_MOUSE) {
758                 int y, x;
759                 MEVENT event;
760
761                 getmouse(&event);
762                 wprintw(win, "KEY_MOUSE, %s", mouse_decode(&event));
763                 getyx(win, y, x);
764                 move(event.y, event.x);
765                 addch('*');
766                 wmove(win, y, x);
767             } else
768 #endif /* NCURSES_MOUSE_VERSION */
769             if (c >= KEY_MIN) {
770 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
771                 if (c == KEY_RESIZE) {
772                     resize_boxes(level, win);
773                 }
774 #endif
775                 (void) waddstr(win, keyname(c));
776             } else if (c > 0x80) {
777                 unsigned c2 = (c & 0x7f);
778                 if (isprint(c2))
779                     (void) wprintw(win, "M-%c", UChar(c2));
780                 else
781                     (void) wprintw(win, "M-%s", unctrl(c2));
782                 waddstr(win, " (high-half character)");
783             } else {
784                 if (isprint(c))
785                     (void) wprintw(win, "%c (ASCII printable character)", c);
786                 else
787                     (void) wprintw(win, "%s (ASCII control character)",
788                                    unctrl(UChar(c)));
789             }
790             wgetch_wrap(win, first_y);
791         }
792     }
793
794     wtimeout(win, -1);
795 }
796
797 static int
798 begin_getch_test(void)
799 {
800     char buf[BUFSIZ];
801     int delay;
802
803     refresh();
804
805 #ifdef NCURSES_MOUSE_VERSION
806     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
807 #endif
808
809     (void) printw("Delay in 10ths of a second (<CR> for blocking input)? ");
810     echo();
811     getnstr(buf, sizeof(buf) - 1);
812     noecho();
813     nonl();
814
815     if (isdigit(UChar(buf[0]))) {
816         delay = atoi(buf) * 100;
817     } else {
818         delay = -1;
819     }
820     raw();
821     move(5, 0);
822     return delay;
823 }
824
825 static void
826 finish_getch_test(void)
827 {
828 #ifdef NCURSES_MOUSE_VERSION
829     mousemask(0, (mmask_t *) 0);
830 #endif
831     erase();
832     noraw();
833     nl();
834     endwin();
835 }
836
837 static void
838 getch_test(void)
839 {
840     int delay = begin_getch_test();
841
842     slk_restore();
843     wgetch_test(0, stdscr, delay);
844     forget_boxes();
845     finish_getch_test();
846 }
847
848 #if USE_WIDEC_SUPPORT
849 /*
850  * For wget_wch_test(), we create pairs of windows - one for a box, one for text.
851  * Resize both and paint the box in the parent.
852  */
853 #if defined(KEY_RESIZE) && HAVE_WRESIZE
854 static void
855 resize_wide_boxes(unsigned level, WINDOW *win)
856 {
857     unsigned n;
858     int base = 5;
859     int high = LINES - base;
860     int wide = COLS;
861
862     touchwin(stdscr);
863     wnoutrefresh(stdscr);
864
865     slk_repaint();
866
867     for (n = 0; n < level; ++n) {
868         wresize(winstack[n].frame, high, wide);
869         wresize(winstack[n].text, high - 2, wide - 2);
870         high -= 2;
871         wide -= 2;
872         werase(winstack[n].text);
873         box_set(winstack[n].frame, 0, 0);
874         wnoutrefresh(winstack[n].frame);
875         wprintw(winstack[n].text,
876                 "size %dx%d\n",
877                 getmaxy(winstack[n].text),
878                 getmaxx(winstack[n].text));
879         wnoutrefresh(winstack[n].text);
880         if (winstack[n].text == win)
881             break;
882     }
883     doupdate();
884 }
885 #endif /* KEY_RESIZE */
886
887 static char *
888 wcstos(const wchar_t *src)
889 {
890     int need;
891     mbstate_t state;
892     char *result = 0;
893     const wchar_t *tmp = src;
894
895     memset(&state, 0, sizeof(state));
896     if ((need = wcsrtombs(0, &tmp, 0, &state)) > 0) {
897         unsigned have = need;
898         result = (char *) calloc(have + 1, 1);
899         tmp = src;
900         if (wcsrtombs(result, &tmp, have, &state) != have) {
901             free(result);
902             result = 0;
903         }
904     }
905     return result;
906 }
907
908 static void
909 wget_wch_test(unsigned level, WINDOW *win, int delay)
910 {
911     wchar_t wchar_buf[BUFSIZ];
912     wint_t wint_buf[BUFSIZ];
913     int first_y, first_x;
914     wint_t c;
915     int incount = 0;
916     bool flags[256];
917     bool blocking = (delay < 0);
918     int y, x, code;
919     char *temp;
920
921     memset(flags, FALSE, sizeof(flags));
922     flags[UChar('k')] = (win == stdscr);
923
924     setup_getch(win, flags);
925     wtimeout(win, delay);
926     getyx(win, first_y, first_x);
927
928     wgetch_help(win, flags);
929     wsetscrreg(win, first_y, getmaxy(win) - 1);
930     scrollok(win, TRUE);
931
932     for (;;) {
933         while ((code = wGet_wchar(win, &c)) == ERR) {
934             incount++;
935             if (blocking) {
936                 (void) wprintw(win, "%05d: input error", incount);
937                 break;
938             } else {
939                 (void) wprintw(win, "%05d: input timed out", incount);
940             }
941             wgetch_wrap(win, first_y);
942         }
943         if (code == ERR && blocking) {
944             wprintw(win, "ERR");
945             wgetch_wrap(win, first_y);
946         } else if (isQuit((int) c)) {
947             break;
948         } else if (c == 'e') {
949             flags[UChar('e')] = !flags[UChar('e')];
950             setup_getch(win, flags);
951             wgetch_help(win, flags);
952         } else if (c == 'g') {
953             waddstr(win, "getstr test: ");
954             echo();
955             code = wgetn_wstr(win, wint_buf, sizeof(wint_buf) - 1);
956             noecho();
957             if (code == ERR) {
958                 wprintw(win, "wgetn_wstr returns an error.");
959             } else {
960                 int n;
961                 for (n = 0; (wchar_buf[n] = wint_buf[n]) != 0; ++n) ;
962                 if ((temp = wcstos(wchar_buf)) != 0) {
963                     wprintw(win, "I saw %d characters:\n\t`%s'.",
964                             (int) wcslen(wchar_buf), temp);
965                     free(temp);
966                 } else {
967                     wprintw(win, "I saw %d characters (cannot convert).",
968                             (int) wcslen(wchar_buf));
969                 }
970             }
971             wclrtoeol(win);
972             wgetch_wrap(win, first_y);
973         } else if (c == 'k') {
974             flags[UChar('k')] = !flags[UChar('k')];
975             setup_getch(win, flags);
976             wgetch_help(win, flags);
977         } else if (c == 'm') {
978             flags[UChar('m')] = !flags[UChar('m')];
979             setup_getch(win, flags);
980             wgetch_help(win, flags);
981         } else if (c == 's') {
982             ShellOut(TRUE);
983         } else if (c == 'w') {
984             int high = getmaxy(win) - 1 - first_y + 1;
985             int wide = getmaxx(win) - first_x;
986             int old_y, old_x;
987             int new_y = first_y + getbegy(win);
988             int new_x = first_x + getbegx(win);
989
990             getyx(win, old_y, old_x);
991             if (high > 2 && wide > 2) {
992                 WINDOW *wb = newwin(high, wide, new_y, new_x);
993                 WINDOW *wi = newwin(high - 2, wide - 2, new_y + 1, new_x + 1);
994
995                 box_set(wb, 0, 0);
996                 wrefresh(wb);
997                 wmove(wi, 0, 0);
998                 remember_boxes(level, wi, wb);
999                 wget_wch_test(level + 1, wi, delay);
1000                 delwin(wi);
1001                 delwin(wb);
1002
1003                 wgetch_help(win, flags);
1004                 wmove(win, old_y, old_x);
1005                 touchwin(win);
1006                 wrefresh(win);
1007             }
1008 #ifdef SIGTSTP
1009         } else if (c == 'z') {
1010             kill(getpid(), SIGTSTP);
1011 #endif
1012         } else {
1013             wprintw(win, "Key pressed: %04o ", c);
1014 #ifdef NCURSES_MOUSE_VERSION
1015             if (c == KEY_MOUSE) {
1016                 MEVENT event;
1017
1018                 getmouse(&event);
1019                 wprintw(win, "KEY_MOUSE, %s", mouse_decode(&event));
1020                 getyx(win, y, x);
1021                 move(event.y, event.x);
1022                 addch('*');
1023                 wmove(win, y, x);
1024             } else
1025 #endif /* NCURSES_MOUSE_VERSION */
1026             if (code == KEY_CODE_YES) {
1027 #if defined(KEY_RESIZE) && HAVE_WRESIZE
1028                 if (c == KEY_RESIZE) {
1029                     resize_wide_boxes(level, win);
1030                 }
1031 #endif
1032                 (void) waddstr(win, key_name((wchar_t) c));
1033             } else {
1034                 if (c < 256 && iscntrl(c)) {
1035                     (void) wprintw(win, "%s (control character)", unctrl(c));
1036                 } else {
1037                     wchar_t c2 = c;
1038                     waddnwstr(win, &c2, 1);
1039                     (void) wprintw(win, " = %#x (printable character)", c);
1040                 }
1041             }
1042             wgetch_wrap(win, first_y);
1043         }
1044     }
1045
1046     wtimeout(win, -1);
1047 }
1048
1049 static void
1050 get_wch_test(void)
1051 {
1052     int delay = begin_getch_test();
1053
1054     slk_restore();
1055     wget_wch_test(0, stdscr, delay);
1056     forget_boxes();
1057     finish_getch_test();
1058 }
1059 #endif
1060
1061 /****************************************************************************
1062  *
1063  * Character attributes test
1064  *
1065  ****************************************************************************/
1066
1067 #if HAVE_SETUPTERM || HAVE_TGETENT
1068 #define get_ncv() TIGETNUM("ncv","NC")
1069 #define get_xmc() TIGETNUM("xmc","sg")
1070 #else
1071 #define get_ncv() -1
1072 #define get_xmc() -1
1073 #endif
1074
1075 #if !HAVE_TERMATTRS
1076 static chtype
1077 my_termattrs(void)
1078 {
1079     static int first = TRUE;
1080     static chtype result = 0;
1081
1082     if (first) {
1083 #if !HAVE_TIGETSTR
1084         char buffer[4096];
1085         char parsed[4096];
1086         char *area_pointer = parsed;
1087
1088         tgetent(buffer, getenv("TERM"));
1089 #endif
1090
1091         if (TIGETSTR("smso", "so"))
1092             result |= A_STANDOUT;
1093         if (TIGETSTR("smul", "us"))
1094             result |= A_UNDERLINE;
1095         if (TIGETSTR("rev", "mr"))
1096             result |= A_REVERSE;
1097         if (TIGETSTR("blink", "mb"))
1098             result |= A_BLINK;
1099         if (TIGETSTR("dim", "mh"))
1100             result |= A_DIM;
1101         if (TIGETSTR("bold", "md"))
1102             result |= A_BOLD;
1103         if (TIGETSTR("smacs", "ac"))
1104             result |= A_ALTCHARSET;
1105
1106         first = FALSE;
1107     }
1108     return result;
1109 }
1110 #define termattrs() my_termattrs()
1111 #endif
1112
1113 #define MAX_ATTRSTRING 31
1114 #define LEN_ATTRSTRING 26
1115
1116 static char attr_test_string[MAX_ATTRSTRING + 1];
1117
1118 static void
1119 attr_legend(WINDOW *helpwin)
1120 {
1121     int row = 1;
1122     int col = 1;
1123
1124     mvwprintw(helpwin, row++, col,
1125               "ESC to exit.");
1126     mvwprintw(helpwin, row++, col,
1127               "^L repaints.");
1128     ++row;
1129     mvwprintw(helpwin, row++, col,
1130               "Modify the test strings:");
1131     mvwprintw(helpwin, row++, col,
1132               "  A digit sets gaps on each side of displayed attributes");
1133     mvwprintw(helpwin, row++, col,
1134               "  </> shifts the text left/right. ");
1135     ++row;
1136     mvwprintw(helpwin, row++, col,
1137               "Toggles:");
1138     if (use_colors) {
1139         mvwprintw(helpwin, row++, col,
1140                   "  f/F/b/F toggle foreground/background background color");
1141         mvwprintw(helpwin, row++, col,
1142                   "  t/T     toggle text/background color attribute");
1143     }
1144     mvwprintw(helpwin, row++, col,
1145               "  a/A     toggle ACS (alternate character set) mapping");
1146     mvwprintw(helpwin, row++, col,
1147               "  v/V     toggle video attribute to combine with each line");
1148 }
1149
1150 static void
1151 show_color_attr(int fg, int bg, int tx)
1152 {
1153     if (use_colors) {
1154         printw("  Colors (fg %d, bg %d", fg, bg);
1155         if (tx >= 0)
1156             printw(", text %d", tx);
1157         printw("),");
1158     }
1159 }
1160
1161 static bool
1162 cycle_color_attr(int ch, short *fg, short *bg, short *tx)
1163 {
1164     bool error = FALSE;
1165
1166     if (use_colors) {
1167         switch (ch) {
1168         case 'f':
1169             *fg = (*fg + 1);
1170             break;
1171         case 'F':
1172             *fg = (*fg - 1);
1173             break;
1174         case 'b':
1175             *bg = (*bg + 1);
1176             break;
1177         case 'B':
1178             *bg = (*bg - 1);
1179             break;
1180         case 't':
1181             *tx = (*tx + 1);
1182             break;
1183         case 'T':
1184             *tx = (*tx - 1);
1185             break;
1186         default:
1187             beep();
1188             error = TRUE;
1189             break;
1190         }
1191         if (*fg >= COLORS)
1192             *fg = min_colors;
1193         if (*fg < min_colors)
1194             *fg = COLORS - 1;
1195         if (*bg >= COLORS)
1196             *bg = min_colors;
1197         if (*bg < min_colors)
1198             *bg = COLORS - 1;
1199         if (*tx >= COLORS)
1200             *tx = -1;
1201         if (*tx < -1)
1202             *tx = COLORS - 1;
1203     } else {
1204         beep();
1205         error = TRUE;
1206     }
1207     return error;
1208 }
1209
1210 static void
1211 adjust_attr_string(int adjust)
1212 {
1213     int first = ((int) UChar(attr_test_string[0])) + adjust;
1214     int last = first + LEN_ATTRSTRING;
1215
1216     if (first >= ' ' && last <= '~') {  /* 32..126 */
1217         int j, k;
1218         for (j = 0, k = first; j < MAX_ATTRSTRING && k <= last; ++j, ++k) {
1219             attr_test_string[j] = k;
1220             if (((k + 1 - first) % 5) == 0) {
1221                 ++j;
1222                 if (j < MAX_ATTRSTRING)
1223                     attr_test_string[j] = ' ';
1224             }
1225         }
1226         while (j < MAX_ATTRSTRING)
1227             attr_test_string[j++] = ' ';
1228         attr_test_string[j] = '\0';
1229     } else {
1230         beep();
1231     }
1232 }
1233
1234 static void
1235 init_attr_string(void)
1236 {
1237     attr_test_string[0] = 'a';
1238     adjust_attr_string(0);
1239 }
1240
1241 static int
1242 show_attr(int row, int skip, bool arrow, chtype attr, const char *name)
1243 {
1244     int ncv = get_ncv();
1245     chtype test = attr & (chtype) (~A_ALTCHARSET);
1246
1247     if (arrow)
1248         mvprintw(row, 5, "-->");
1249     mvprintw(row, 8, "%s mode:", name);
1250     mvprintw(row, 24, "|");
1251     if (skip)
1252         printw("%*s", skip, " ");
1253     /*
1254      * Just for testing, write text using the alternate character set one
1255      * character at a time (to pass its rendition directly), and use the
1256      * string operation for the other attributes.
1257      */
1258     if (attr & A_ALTCHARSET) {
1259         const char *s;
1260         chtype ch;
1261
1262         for (s = attr_test_string; *s != '\0'; ++s) {
1263             ch = UChar(*s);
1264             addch(ch | attr);
1265         }
1266     } else {
1267         attrset(attr);
1268         addstr(attr_test_string);
1269         attroff(attr);
1270     }
1271     if (skip)
1272         printw("%*s", skip, " ");
1273     printw("|");
1274     if (test != A_NORMAL) {
1275         if (!(termattrs() & test)) {
1276             printw(" (N/A)");
1277         } else {
1278             if (ncv > 0 && (getbkgd(stdscr) & A_COLOR)) {
1279                 static const chtype table[] =
1280                 {
1281                     A_STANDOUT,
1282                     A_UNDERLINE,
1283                     A_REVERSE,
1284                     A_BLINK,
1285                     A_DIM,
1286                     A_BOLD,
1287 #ifdef A_INVIS
1288                     A_INVIS,
1289 #endif
1290                     A_PROTECT,
1291                     A_ALTCHARSET
1292                 };
1293                 unsigned n;
1294                 bool found = FALSE;
1295                 for (n = 0; n < SIZEOF(table); n++) {
1296                     if ((table[n] & attr) != 0
1297                         && ((1 << n) & ncv) != 0) {
1298                         found = TRUE;
1299                         break;
1300                     }
1301                 }
1302                 if (found)
1303                     printw(" (NCV)");
1304             }
1305             if ((termattrs() & test) != test)
1306                 printw(" (Part)");
1307         }
1308     }
1309     return row + 2;
1310 }
1311 /* *INDENT-OFF* */
1312 static const struct {
1313     chtype                      attr;
1314     NCURSES_CONST char *        name;
1315 } attrs_to_test[] = {
1316     { A_STANDOUT,       "STANDOUT" },
1317     { A_REVERSE,        "REVERSE" },
1318     { A_BOLD,           "BOLD" },
1319     { A_UNDERLINE,      "UNDERLINE" },
1320     { A_DIM,            "DIM" },
1321     { A_BLINK,          "BLINK" },
1322     { A_PROTECT,        "PROTECT" },
1323 #ifdef A_INVIS
1324     { A_INVIS,          "INVISIBLE" },
1325 #endif
1326     { A_NORMAL,         "NORMAL" },
1327 };
1328 /* *INDENT-ON* */
1329
1330 static bool
1331 attr_getc(int *skip, short *fg, short *bg, short *tx, int *ac, unsigned *kc)
1332 {
1333     bool result = TRUE;
1334     bool error = FALSE;
1335     WINDOW *helpwin;
1336
1337     do {
1338         int ch = Getchar();
1339
1340         error = FALSE;
1341         if (ch < 256 && isdigit(ch)) {
1342             *skip = (ch - '0');
1343         } else {
1344             switch (ch) {
1345             case CTRL('L'):
1346                 Repaint();
1347                 break;
1348             case '?':
1349                 if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
1350                     box(helpwin, 0, 0);
1351                     attr_legend(helpwin);
1352                     wGetchar(helpwin);
1353                     delwin(helpwin);
1354                 }
1355                 break;
1356             case 'a':
1357                 *ac = 0;
1358                 break;
1359             case 'A':
1360                 *ac = A_ALTCHARSET;
1361                 break;
1362             case 'v':
1363                 if (*kc == 0)
1364                     *kc = SIZEOF(attrs_to_test) - 1;
1365                 else
1366                     *kc -= 1;
1367                 break;
1368             case 'V':
1369                 *kc += 1;
1370                 if (*kc >= SIZEOF(attrs_to_test))
1371                     *kc = 0;
1372                 break;
1373             case '<':
1374                 adjust_attr_string(-1);
1375                 break;
1376             case '>':
1377                 adjust_attr_string(1);
1378                 break;
1379               case_QUIT:
1380                 result = FALSE;
1381                 break;
1382             default:
1383                 error = cycle_color_attr(ch, fg, bg, tx);
1384                 break;
1385             }
1386         }
1387     } while (error);
1388     return result;
1389 }
1390
1391 static void
1392 attr_test(void)
1393 /* test text attributes */
1394 {
1395     int n;
1396     int skip = get_xmc();
1397     short fg = COLOR_BLACK;     /* color pair 0 is special */
1398     short bg = COLOR_BLACK;
1399     short tx = -1;
1400     int ac = 0;
1401     unsigned j, k;
1402
1403     if (skip < 0)
1404         skip = 0;
1405
1406     n = skip;                   /* make it easy */
1407     k = SIZEOF(attrs_to_test) - 1;
1408     init_attr_string();
1409
1410     do {
1411         int row = 2;
1412         chtype normal = A_NORMAL | BLANK;
1413         chtype extras = ac;
1414
1415         if (use_colors) {
1416             short pair = (fg != COLOR_BLACK || bg != COLOR_BLACK);
1417             if (pair != 0) {
1418                 pair = 1;
1419                 if (init_pair(pair, fg, bg) == ERR) {
1420                     beep();
1421                 } else {
1422                     normal |= COLOR_PAIR(pair);
1423                 }
1424             }
1425             if (tx >= 0) {
1426                 pair = 2;
1427                 if (init_pair(pair, tx, bg) == ERR) {
1428                     beep();
1429                 } else {
1430                     extras |= COLOR_PAIR(pair);
1431                 }
1432             }
1433         }
1434         bkgd(normal);
1435         bkgdset(normal);
1436         erase();
1437
1438         box(stdscr, 0, 0);
1439         mvaddstr(0, 20, "Character attribute test display");
1440
1441         for (j = 0; j < SIZEOF(attrs_to_test); ++j) {
1442             bool arrow = (j == k);
1443             row = show_attr(row, n, arrow,
1444                             extras |
1445                             attrs_to_test[j].attr |
1446                             attrs_to_test[k].attr,
1447                             attrs_to_test[j].name);
1448         }
1449
1450         mvprintw(row, 8,
1451                  "This terminal does %shave the magic-cookie glitch",
1452                  get_xmc() > -1 ? "" : "not ");
1453         mvprintw(row + 1, 8, "Enter '?' for help.");
1454         show_color_attr(fg, bg, tx);
1455         printw("  ACS (%d)", ac != 0);
1456
1457         refresh();
1458     } while (attr_getc(&n, &fg, &bg, &tx, &ac, &k));
1459
1460     bkgdset(A_NORMAL | BLANK);
1461     erase();
1462     endwin();
1463 }
1464
1465 #if USE_WIDEC_SUPPORT
1466 static wchar_t wide_attr_test_string[MAX_ATTRSTRING + 1];
1467
1468 static void
1469 wide_adjust_attr_string(int adjust)
1470 {
1471     int first = ((int) UChar(wide_attr_test_string[0])) + adjust;
1472     int last = first + LEN_ATTRSTRING;
1473
1474     if (first >= ' ' && last <= '~') {  /* 32..126 */
1475         int j, k;
1476         for (j = 0, k = first; j < MAX_ATTRSTRING && k <= last; ++j, ++k) {
1477             wide_attr_test_string[j] = k;
1478             if (((k + 1 - first) % 5) == 0) {
1479                 ++j;
1480                 if (j < MAX_ATTRSTRING)
1481                     wide_attr_test_string[j] = ' ';
1482             }
1483         }
1484         while (j < MAX_ATTRSTRING)
1485             wide_attr_test_string[j++] = ' ';
1486         wide_attr_test_string[j] = '\0';
1487     } else {
1488         beep();
1489     }
1490 }
1491
1492 static void
1493 wide_init_attr_string(void)
1494 {
1495     wide_attr_test_string[0] = 'a';
1496     wide_adjust_attr_string(0);
1497 }
1498
1499 static void
1500 set_wide_background(short pair)
1501 {
1502     cchar_t normal;
1503     wchar_t blank[2];
1504
1505     blank[0] = ' ';
1506     blank[1] = 0;
1507     setcchar(&normal, blank, A_NORMAL, pair, 0);
1508     bkgrnd(&normal);
1509     bkgrndset(&normal);
1510 }
1511
1512 static attr_t
1513 get_wide_background(void)
1514 {
1515     attr_t result = A_NORMAL;
1516     attr_t attr;
1517     cchar_t ch;
1518     short pair;
1519     wchar_t wch[10];
1520
1521     if (getbkgrnd(&ch) != ERR) {
1522         if (getcchar(&ch, wch, &attr, &pair, 0) != ERR) {
1523             result = attr;
1524         }
1525     }
1526     return result;
1527 }
1528
1529 static int
1530 wide_show_attr(int row, int skip, bool arrow, chtype attr, short pair, const char *name)
1531 {
1532     int ncv = get_ncv();
1533     chtype test = attr & ~WA_ALTCHARSET;
1534
1535     if (arrow)
1536         mvprintw(row, 5, "-->");
1537     mvprintw(row, 8, "%s mode:", name);
1538     mvprintw(row, 24, "|");
1539     if (skip)
1540         printw("%*s", skip, " ");
1541
1542     /*
1543      * Just for testing, write text using the alternate character set one
1544      * character at a time (to pass its rendition directly), and use the
1545      * string operation for the other attributes.
1546      */
1547     if (attr & WA_ALTCHARSET) {
1548         const wchar_t *s;
1549         cchar_t ch;
1550
1551         for (s = wide_attr_test_string; *s != L'\0'; ++s) {
1552             wchar_t fill[2];
1553             fill[0] = *s;
1554             fill[1] = L'\0';
1555             setcchar(&ch, fill, attr, pair, 0);
1556             add_wch(&ch);
1557         }
1558     } else {
1559         attr_t old_attr;
1560         short old_pair;
1561
1562         attr_get(&old_attr, &old_pair, 0);
1563         attr_set(attr, pair, 0);
1564         addwstr(wide_attr_test_string);
1565         attr_set(old_attr, old_pair, 0);
1566     }
1567     if (skip)
1568         printw("%*s", skip, " ");
1569     printw("|");
1570     if (test != A_NORMAL) {
1571         if (!(term_attrs() & test)) {
1572             printw(" (N/A)");
1573         } else {
1574             if (ncv > 0 && (get_wide_background() & A_COLOR)) {
1575                 static const attr_t table[] =
1576                 {
1577                     WA_STANDOUT,
1578                     WA_UNDERLINE,
1579                     WA_REVERSE,
1580                     WA_BLINK,
1581                     WA_DIM,
1582                     WA_BOLD,
1583                     WA_INVIS,
1584                     WA_PROTECT,
1585                     WA_ALTCHARSET
1586                 };
1587                 unsigned n;
1588                 bool found = FALSE;
1589                 for (n = 0; n < SIZEOF(table); n++) {
1590                     if ((table[n] & attr) != 0
1591                         && ((1 << n) & ncv) != 0) {
1592                         found = TRUE;
1593                         break;
1594                     }
1595                 }
1596                 if (found)
1597                     printw(" (NCV)");
1598             }
1599             if ((term_attrs() & test) != test)
1600                 printw(" (Part)");
1601         }
1602     }
1603     return row + 2;
1604 }
1605
1606 static bool
1607 wide_attr_getc(int *skip, short *fg, short *bg, short *tx, int *ac, unsigned *kc)
1608 {
1609     bool result = TRUE;
1610     bool error = FALSE;
1611     WINDOW *helpwin;
1612
1613     do {
1614         int ch = Getchar();
1615
1616         error = FALSE;
1617         if (ch < 256 && isdigit(ch)) {
1618             *skip = (ch - '0');
1619         } else {
1620             switch (ch) {
1621             case CTRL('L'):
1622                 Repaint();
1623                 break;
1624             case '?':
1625                 if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
1626                     box_set(helpwin, 0, 0);
1627                     attr_legend(helpwin);
1628                     wGetchar(helpwin);
1629                     delwin(helpwin);
1630                 }
1631                 break;
1632             case 'a':
1633                 *ac = 0;
1634                 break;
1635             case 'A':
1636                 *ac = A_ALTCHARSET;
1637                 break;
1638             case 'v':
1639                 if (*kc == 0)
1640                     *kc = SIZEOF(attrs_to_test) - 1;
1641                 else
1642                     *kc -= 1;
1643                 break;
1644             case 'V':
1645                 *kc += 1;
1646                 if (*kc >= SIZEOF(attrs_to_test))
1647                     *kc = 0;
1648                 break;
1649             case '<':
1650                 wide_adjust_attr_string(-1);
1651                 break;
1652             case '>':
1653                 wide_adjust_attr_string(1);
1654                 break;
1655               case_QUIT:
1656                 result = FALSE;
1657                 break;
1658             default:
1659                 error = cycle_color_attr(ch, fg, bg, tx);
1660                 break;
1661             }
1662         }
1663     } while (error);
1664     return result;
1665 }
1666
1667 static void
1668 wide_attr_test(void)
1669 /* test text attributes using wide-character calls */
1670 {
1671     int n;
1672     int skip = get_xmc();
1673     short fg = COLOR_BLACK;     /* color pair 0 is special */
1674     short bg = COLOR_BLACK;
1675     short tx = -1;
1676     int ac = 0;
1677     unsigned j, k;
1678
1679     if (skip < 0)
1680         skip = 0;
1681
1682     n = skip;                   /* make it easy */
1683     k = SIZEOF(attrs_to_test) - 1;
1684     wide_init_attr_string();
1685
1686     do {
1687         int row = 2;
1688         short pair = 0;
1689         short extras = 0;
1690
1691         if (use_colors) {
1692             pair = (fg != COLOR_BLACK || bg != COLOR_BLACK);
1693             if (pair != 0) {
1694                 pair = 1;
1695                 if (init_pair(pair, fg, bg) == ERR) {
1696                     beep();
1697                 }
1698             }
1699             extras = pair;
1700             if (tx >= 0) {
1701                 extras = 2;
1702                 if (init_pair(extras, tx, bg) == ERR) {
1703                     beep();
1704                 }
1705             }
1706         }
1707         set_wide_background(pair);
1708         erase();
1709
1710         box_set(stdscr, 0, 0);
1711         mvaddstr(0, 20, "Character attribute test display");
1712
1713         for (j = 0; j < SIZEOF(attrs_to_test); ++j) {
1714             row = wide_show_attr(row, n, j == k,
1715                                  ac |
1716                                  attrs_to_test[j].attr |
1717                                  attrs_to_test[k].attr,
1718                                  extras,
1719                                  attrs_to_test[j].name);
1720         }
1721
1722         mvprintw(row, 8,
1723                  "This terminal does %shave the magic-cookie glitch",
1724                  get_xmc() > -1 ? "" : "not ");
1725         mvprintw(row + 1, 8, "Enter '?' for help.");
1726         show_color_attr(fg, bg, tx);
1727         printw("  ACS (%d)", ac != 0);
1728
1729         refresh();
1730     } while (wide_attr_getc(&n, &fg, &bg, &tx, &ac, &k));
1731
1732     set_wide_background(0);
1733     erase();
1734     endwin();
1735 }
1736 #endif
1737
1738 /****************************************************************************
1739  *
1740  * Color support tests
1741  *
1742  ****************************************************************************/
1743
1744 static NCURSES_CONST char *the_color_names[] =
1745 {
1746     "black",
1747     "red",
1748     "green",
1749     "yellow",
1750     "blue",
1751     "magenta",
1752     "cyan",
1753     "white",
1754     "BLACK",
1755     "RED",
1756     "GREEN",
1757     "YELLOW",
1758     "BLUE",
1759     "MAGENTA",
1760     "CYAN",
1761     "WHITE"
1762 };
1763
1764 static void
1765 show_color_name(int y, int x, int color, bool wide)
1766 {
1767     if (move(y, x) != ERR) {
1768         char temp[80];
1769         int width = 8;
1770
1771         if (wide) {
1772             sprintf(temp, "%02d", color);
1773             width = 4;
1774         } else if (color >= 8) {
1775             sprintf(temp, "[%02d]", color);
1776         } else {
1777             strcpy(temp, the_color_names[color]);
1778         }
1779         printw("%-*.*s", width, width, temp);
1780     }
1781 }
1782
1783 static void
1784 color_legend(WINDOW *helpwin)
1785 {
1786     int row = 1;
1787     int col = 1;
1788
1789     mvwprintw(helpwin, row++, col,
1790               "ESC to exit.");
1791     ++row;
1792     mvwprintw(helpwin, row++, col,
1793               "Use up/down arrow to scroll through the display if it is");
1794     mvwprintw(helpwin, row++, col,
1795               "longer than one screen. Control/N and Control/P can be used");
1796     mvwprintw(helpwin, row++, col,
1797               "in place up up/down arrow.  Use pageup/pagedown to scroll a");
1798     mvwprintw(helpwin, row++, col,
1799               "full screen; control/B and control/F can be used here.");
1800     ++row;
1801     mvwprintw(helpwin, row++, col,
1802               "Toggles:");
1803     mvwprintw(helpwin, row++, col,
1804               "  b/B     toggle bold off/on");
1805     mvwprintw(helpwin, row++, col,
1806               "  n/N     toggle text/number on/off");
1807     mvwprintw(helpwin, row++, col,
1808               "  w/W     toggle width between 8/16 colors");
1809 }
1810
1811 #define set_color_test(name, value) if (name != value) { name = value; base_row = 0; }
1812
1813 /* generate a color test pattern */
1814 static void
1815 color_test(void)
1816 {
1817     short i;
1818     int top = 0, width;
1819     int base_row = 0;
1820     int grid_top = top + 3;
1821     int page_size = (LINES - grid_top);
1822     int pairs_max = PAIR_NUMBER(A_COLOR) + 1;
1823     int row_limit;
1824     int per_row;
1825     char numbered[80];
1826     const char *hello;
1827     bool done = FALSE;
1828     bool opt_bold = FALSE;
1829     bool opt_wide = FALSE;
1830     bool opt_nums = FALSE;
1831     WINDOW *helpwin;
1832
1833     if (pairs_max > COLOR_PAIRS)
1834         pairs_max = COLOR_PAIRS;
1835
1836     while (!done) {
1837         int shown = 0;
1838
1839         /* this assumes an 80-column line */
1840         if (opt_wide) {
1841             width = 4;
1842             hello = "Test";
1843             per_row = (COLORS > 8) ? 16 : 8;
1844         } else {
1845             width = 8;
1846             hello = "Hello";
1847             per_row = 8;
1848         }
1849
1850         row_limit = (pairs_max + per_row - 1) / per_row;
1851
1852         move(0, 0);
1853         (void) printw("There are %d color pairs and %d colors\n",
1854                       pairs_max, COLORS);
1855
1856         clrtobot();
1857         (void) mvprintw(top + 1, 0,
1858                         "%dx%d matrix of foreground/background colors, bold *%s*\n",
1859                         row_limit,
1860                         per_row,
1861                         opt_bold ? "on" : "off");
1862
1863         /* show color names/numbers across the top */
1864         for (i = 0; i < per_row; i++)
1865             show_color_name(top + 2, (i + 1) * width, i, opt_wide);
1866
1867         /* show a grid of colors, with color names/ numbers on the left */
1868         for (i = (base_row * per_row); i < pairs_max; i++) {
1869             int row = grid_top + (i / per_row) - base_row;
1870             int col = (i % per_row + 1) * width;
1871             short pair = i;
1872
1873             if (row >= 0 && move(row, col) != ERR) {
1874                 short fg = i % COLORS;
1875                 short bg = i / COLORS;
1876
1877                 init_pair(pair, fg, bg);
1878                 attron((attr_t) COLOR_PAIR(pair));
1879                 if (opt_bold)
1880                     attron((attr_t) A_BOLD);
1881
1882                 if (opt_nums) {
1883                     sprintf(numbered, "{%02X}", i);
1884                     hello = numbered;
1885                 }
1886                 printw("%-*.*s", width, width, hello);
1887                 attrset(A_NORMAL);
1888
1889                 if ((i % per_row) == 0 && (i % COLORS) == 0) {
1890                     show_color_name(row, 0, i / COLORS, opt_wide);
1891                 }
1892                 ++shown;
1893             } else if (shown) {
1894                 break;
1895             }
1896         }
1897
1898         switch (wGetchar(stdscr)) {
1899         case 'b':
1900             opt_bold = FALSE;
1901             break;
1902         case 'B':
1903             opt_bold = TRUE;
1904             break;
1905         case 'n':
1906             opt_nums = FALSE;
1907             break;
1908         case 'N':
1909             opt_nums = TRUE;
1910             break;
1911           case_QUIT:
1912             done = TRUE;
1913             continue;
1914         case 'w':
1915             set_color_test(opt_wide, FALSE);
1916             break;
1917         case 'W':
1918             set_color_test(opt_wide, TRUE);
1919             break;
1920         case CTRL('p'):
1921         case KEY_UP:
1922             if (base_row <= 0) {
1923                 beep();
1924             } else {
1925                 base_row -= 1;
1926             }
1927             break;
1928         case CTRL('n'):
1929         case KEY_DOWN:
1930             if (base_row + page_size >= row_limit) {
1931                 beep();
1932             } else {
1933                 base_row += 1;
1934             }
1935             break;
1936         case CTRL('b'):
1937         case KEY_PREVIOUS:
1938         case KEY_PPAGE:
1939             if (base_row <= 0) {
1940                 beep();
1941             } else {
1942                 base_row -= (page_size - 1);
1943                 if (base_row < 0)
1944                     base_row = 0;
1945             }
1946             break;
1947         case CTRL('f'):
1948         case KEY_NEXT:
1949         case KEY_NPAGE:
1950             if (base_row + page_size >= row_limit) {
1951                 beep();
1952             } else {
1953                 base_row += page_size - 1;
1954                 if (base_row + page_size >= row_limit) {
1955                     base_row = row_limit - page_size - 1;
1956                 }
1957             }
1958             break;
1959         case '?':
1960             if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
1961                 box(helpwin, 0, 0);
1962                 color_legend(helpwin);
1963                 wGetchar(helpwin);
1964                 delwin(helpwin);
1965             }
1966             break;
1967         default:
1968             beep();
1969             continue;
1970         }
1971     }
1972
1973     erase();
1974     endwin();
1975 }
1976
1977 #if USE_WIDEC_SUPPORT
1978 /* generate a color test pattern */
1979 static void
1980 wide_color_test(void)
1981 {
1982     int c;
1983     int i;
1984     int top = 0, width;
1985     int base_row = 0;
1986     int grid_top = top + 3;
1987     int page_size = (LINES - grid_top);
1988     int pairs_max = COLOR_PAIRS;
1989     int row_limit;
1990     int per_row;
1991     char numbered[80];
1992     const char *hello;
1993     bool done = FALSE;
1994     bool opt_bold = FALSE;
1995     bool opt_wide = FALSE;
1996     bool opt_nums = FALSE;
1997     WINDOW *helpwin;
1998
1999     while (!done) {
2000         int shown = 0;
2001
2002         /* this assumes an 80-column line */
2003         if (opt_wide) {
2004             width = 4;
2005             hello = "Test";
2006             per_row = (COLORS > 8) ? 16 : 8;
2007         } else {
2008             width = 8;
2009             hello = "Hello";
2010             per_row = 8;
2011         }
2012
2013         row_limit = (pairs_max + per_row - 1) / per_row;
2014
2015         move(0, 0);
2016         (void) printw("There are %d color pairs and %d colors\n",
2017                       pairs_max, COLORS);
2018
2019         clrtobot();
2020         (void) mvprintw(top + 1, 0,
2021                         "%dx%d matrix of foreground/background colors, bold *%s*\n",
2022                         row_limit,
2023                         per_row,
2024                         opt_bold ? "on" : "off");
2025
2026         /* show color names/numbers across the top */
2027         for (i = 0; i < per_row; i++)
2028             show_color_name(top + 2, (i + 1) * width, i, opt_wide);
2029
2030         /* show a grid of colors, with color names/ numbers on the left */
2031         for (i = (base_row * per_row); i < pairs_max; i++) {
2032             int row = grid_top + (i / per_row) - base_row;
2033             int col = (i % per_row + 1) * width;
2034             int pair = i;
2035
2036             if (row >= 0 && move(row, col) != ERR) {
2037                 init_pair(pair, i % COLORS, i / COLORS);
2038                 color_set(pair, NULL);
2039                 if (opt_bold)
2040                     attr_on((attr_t) A_BOLD, NULL);
2041
2042                 if (opt_nums) {
2043                     sprintf(numbered, "{%02X}", i);
2044                     hello = numbered;
2045                 }
2046                 printw("%-*.*s", width, width, hello);
2047                 attr_set(A_NORMAL, 0, NULL);
2048
2049                 if ((i % per_row) == 0 && (i % COLORS) == 0) {
2050                     show_color_name(row, 0, i / COLORS, opt_wide);
2051                 }
2052                 ++shown;
2053             } else if (shown) {
2054                 break;
2055             }
2056         }
2057
2058         switch (c = wGetchar(stdscr)) {
2059         case 'b':
2060             opt_bold = FALSE;
2061             break;
2062         case 'B':
2063             opt_bold = TRUE;
2064             break;
2065         case 'n':
2066             opt_nums = FALSE;
2067             break;
2068         case 'N':
2069             opt_nums = TRUE;
2070             break;
2071           case_QUIT:
2072             done = TRUE;
2073             continue;
2074         case 'w':
2075             set_color_test(opt_wide, FALSE);
2076             break;
2077         case 'W':
2078             set_color_test(opt_wide, TRUE);
2079             break;
2080         case CTRL('p'):
2081         case KEY_UP:
2082             if (base_row <= 0) {
2083                 beep();
2084             } else {
2085                 base_row -= 1;
2086             }
2087             break;
2088         case CTRL('n'):
2089         case KEY_DOWN:
2090             if (base_row + page_size >= row_limit) {
2091                 beep();
2092             } else {
2093                 base_row += 1;
2094             }
2095             break;
2096         case CTRL('b'):
2097         case KEY_PREVIOUS:
2098         case KEY_PPAGE:
2099             if (base_row <= 0) {
2100                 beep();
2101             } else {
2102                 base_row -= (page_size - 1);
2103                 if (base_row < 0)
2104                     base_row = 0;
2105             }
2106             break;
2107         case CTRL('f'):
2108         case KEY_NEXT:
2109         case KEY_NPAGE:
2110             if (base_row + page_size >= row_limit) {
2111                 beep();
2112             } else {
2113                 base_row += page_size - 1;
2114                 if (base_row + page_size >= row_limit) {
2115                     base_row = row_limit - page_size - 1;
2116                 }
2117             }
2118             break;
2119         case '?':
2120             if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
2121                 box(helpwin, 0, 0);
2122                 color_legend(helpwin);
2123                 wGetchar(helpwin);
2124                 delwin(helpwin);
2125             }
2126             break;
2127         default:
2128             beep();
2129             continue;
2130         }
2131     }
2132
2133     erase();
2134     endwin();
2135 }
2136 #endif /* USE_WIDEC_SUPPORT */
2137
2138 static void
2139 change_color(short current, int field, int value, int usebase)
2140 {
2141     short red, green, blue;
2142
2143     color_content(current, &red, &green, &blue);
2144
2145     switch (field) {
2146     case 0:
2147         red = usebase ? red + value : value;
2148         break;
2149     case 1:
2150         green = usebase ? green + value : value;
2151         break;
2152     case 2:
2153         blue = usebase ? blue + value : value;
2154         break;
2155     }
2156
2157     if (init_color(current, red, green, blue) == ERR)
2158         beep();
2159 }
2160
2161 static void
2162 init_all_colors(void)
2163 {
2164     short c;
2165
2166     for (c = 0; c < COLORS; ++c)
2167         init_color(c,
2168                    all_colors[c].red,
2169                    all_colors[c].green,
2170                    all_colors[c].blue);
2171 }
2172
2173 #define scaled_rgb(n) ((255 * (n)) / 1000)
2174
2175 static void
2176 color_edit(void)
2177 /* display the color test pattern, without trying to edit colors */
2178 {
2179     short i;
2180     short current = 0;
2181     int this_c = 0, value = 0, field = 0;
2182     int last_c;
2183     int top_color = 0;
2184     int page_size = (LINES - 6);
2185
2186     init_all_colors();
2187     refresh();
2188
2189     for (i = 0; i < max_colors; i++)
2190         init_pair(i, COLOR_WHITE, i);
2191
2192     mvprintw(LINES - 2, 0, "Number: %d", value);
2193
2194     do {
2195         short red, green, blue;
2196
2197         attron(A_BOLD);
2198         mvaddstr(0, 20, "Color RGB Value Editing");
2199         attroff(A_BOLD);
2200
2201         for (i = top_color;
2202              (i - top_color < page_size)
2203              && (i < max_colors); i++) {
2204             char numeric[80];
2205
2206             sprintf(numeric, "[%d]", i);
2207             mvprintw(2 + i - top_color, 0, "%c %-8s:",
2208                      (i == current ? '>' : ' '),
2209                      (i < (int) SIZEOF(the_color_names)
2210                       ? the_color_names[i] : numeric));
2211             attrset(COLOR_PAIR(i));
2212             addstr("        ");
2213             attrset(A_NORMAL);
2214
2215             color_content(i, &red, &green, &blue);
2216             addstr("   R = ");
2217             if (current == i && field == 0)
2218                 attron(A_STANDOUT);
2219             printw("%04d", red);
2220             if (current == i && field == 0)
2221                 attrset(A_NORMAL);
2222             addstr(", G = ");
2223             if (current == i && field == 1)
2224                 attron(A_STANDOUT);
2225             printw("%04d", green);
2226             if (current == i && field == 1)
2227                 attrset(A_NORMAL);
2228             addstr(", B = ");
2229             if (current == i && field == 2)
2230                 attron(A_STANDOUT);
2231             printw("%04d", blue);
2232             if (current == i && field == 2)
2233                 attrset(A_NORMAL);
2234             attrset(A_NORMAL);
2235             printw(" ( %3d %3d %3d )",
2236                    scaled_rgb(red),
2237                    scaled_rgb(green),
2238                    scaled_rgb(blue));
2239         }
2240
2241         mvaddstr(LINES - 3, 0,
2242                  "Use up/down to select a color, left/right to change fields.");
2243         mvaddstr(LINES - 2, 0,
2244                  "Modify field by typing nnn=, nnn-, or nnn+.  ? for help.");
2245
2246         move(2 + current - top_color, 0);
2247
2248         last_c = this_c;
2249         this_c = Getchar();
2250         if (this_c < 256 && isdigit(this_c) && !isdigit(last_c))
2251             value = 0;
2252
2253         switch (this_c) {
2254         case CTRL('b'):
2255         case KEY_PPAGE:
2256             if (current > 0)
2257                 current -= (page_size - 1);
2258             else
2259                 beep();
2260             break;
2261
2262         case CTRL('f'):
2263         case KEY_NPAGE:
2264             if (current < (max_colors - 1))
2265                 current += (page_size - 1);
2266             else
2267                 beep();
2268             break;
2269
2270         case CTRL('p'):
2271         case KEY_UP:
2272             current = (current == 0 ? (max_colors - 1) : current - 1);
2273             break;
2274
2275         case CTRL('n'):
2276         case KEY_DOWN:
2277             current = (current == (max_colors - 1) ? 0 : current + 1);
2278             break;
2279
2280         case KEY_RIGHT:
2281             field = (field == 2 ? 0 : field + 1);
2282             break;
2283
2284         case KEY_LEFT:
2285             field = (field == 0 ? 2 : field - 1);
2286             break;
2287
2288         case '0':
2289         case '1':
2290         case '2':
2291         case '3':
2292         case '4':
2293         case '5':
2294         case '6':
2295         case '7':
2296         case '8':
2297         case '9':
2298             value = value * 10 + (this_c - '0');
2299             break;
2300
2301         case '+':
2302             change_color(current, field, value, 1);
2303             break;
2304
2305         case '-':
2306             change_color(current, field, -value, 1);
2307             break;
2308
2309         case '=':
2310             change_color(current, field, value, 0);
2311             break;
2312
2313         case '?':
2314             erase();
2315             P("                      RGB Value Editing Help");
2316             P("");
2317             P("You are in the RGB value editor.  Use the arrow keys to select one of");
2318             P("the fields in one of the RGB triples of the current colors; the one");
2319             P("currently selected will be reverse-video highlighted.");
2320             P("");
2321             P("To change a field, enter the digits of the new value; they are echoed");
2322             P("as entered.  Finish by typing `='.  The change will take effect instantly.");
2323             P("To increment or decrement a value, use the same procedure, but finish");
2324             P("with a `+' or `-'.");
2325             P("");
2326             P("Press 'm' to invoke the top-level menu with the current color settings.");
2327             P("To quit, do ESC");
2328
2329             Pause();
2330             erase();
2331             break;
2332
2333         case 'm':
2334             endwin();
2335             main_menu(FALSE);
2336             refresh();
2337             break;
2338
2339           case_QUIT:
2340             break;
2341
2342         default:
2343             beep();
2344             break;
2345         }
2346
2347         if (current < 0)
2348             current = 0;
2349         if (current >= max_colors)
2350             current = max_colors - 1;
2351         if (current < top_color)
2352             top_color = current;
2353         if (current - top_color >= page_size)
2354             top_color = current - (page_size - 1);
2355
2356         mvprintw(LINES - 1, 0, "Number: %d", value);
2357         clrtoeol();
2358     } while
2359         (!isQuit(this_c));
2360
2361     erase();
2362
2363     /*
2364      * ncurses does not reset each color individually when calling endwin().
2365      */
2366     init_all_colors();
2367
2368     endwin();
2369 }
2370
2371 /****************************************************************************
2372  *
2373  * Soft-key label test
2374  *
2375  ****************************************************************************/
2376
2377 #if USE_SOFTKEYS
2378
2379 #define SLK_HELP 17
2380 #define SLK_WORK (SLK_HELP + 3)
2381
2382 static void
2383 slk_help(void)
2384 {
2385     static const char *table[] =
2386     {
2387         "Available commands are:"
2388         ,""
2389         ,"^L         -- repaint this message and activate soft keys"
2390         ,"a/d        -- activate/disable soft keys"
2391         ,"c          -- set centered format for labels"
2392         ,"l          -- set left-justified format for labels"
2393         ,"r          -- set right-justified format for labels"
2394         ,"[12345678] -- set label; labels are numbered 1 through 8"
2395         ,"e          -- erase stdscr (should not erase labels)"
2396         ,"s          -- test scrolling of shortened screen"
2397 #if HAVE_SLK_COLOR
2398         ,"F/B        -- cycle through foreground/background colors"
2399 #endif
2400         ,"ESC  -- return to main menu"
2401         ,""
2402         ,"Note: if activating the soft keys causes your terminal to scroll up"
2403         ,"one line, your terminal auto-scrolls when anything is written to the"
2404         ,"last screen position.  The ncurses code does not yet handle this"
2405         ,"gracefully."
2406     };
2407     unsigned j;
2408
2409     move(2, 0);
2410     for (j = 0; j < SIZEOF(table); ++j) {
2411         P(table[j]);
2412     }
2413     refresh();
2414 }
2415
2416 #if HAVE_SLK_COLOR
2417 static void
2418 call_slk_color(short fg, short bg)
2419 {
2420     init_pair(1, bg, fg);
2421     slk_color(1);
2422     mvprintw(SLK_WORK, 0, "Colors %d/%d\n", fg, bg);
2423     clrtoeol();
2424     refresh();
2425 }
2426 #endif
2427
2428 static void
2429 slk_test(void)
2430 /* exercise the soft keys */
2431 {
2432     int c, fmt = 1;
2433     char buf[9];
2434     char *s;
2435 #if HAVE_SLK_COLOR
2436     short fg = COLOR_BLACK;
2437     short bg = COLOR_WHITE;
2438 #endif
2439
2440     c = CTRL('l');
2441 #if HAVE_SLK_COLOR
2442     if (use_colors) {
2443         call_slk_color(fg, bg);
2444     }
2445 #endif
2446
2447     do {
2448         move(0, 0);
2449         switch (c) {
2450         case CTRL('l'):
2451             erase();
2452             attron(A_BOLD);
2453             mvaddstr(0, 20, "Soft Key Exerciser");
2454             attroff(A_BOLD);
2455
2456             slk_help();
2457             /* fall through */
2458
2459         case 'a':
2460             slk_restore();
2461             break;
2462
2463         case 'e':
2464             wclear(stdscr);
2465             break;
2466
2467         case 's':
2468             mvprintw(SLK_WORK, 0, "Press Q to stop the scrolling-test: ");
2469             while ((c = Getchar()) != 'Q' && (c != ERR))
2470                 addch((chtype) c);
2471             break;
2472
2473         case 'd':
2474             slk_clear();
2475             break;
2476
2477         case 'l':
2478             fmt = 0;
2479             break;
2480
2481         case 'c':
2482             fmt = 1;
2483             break;
2484
2485         case 'r':
2486             fmt = 2;
2487             break;
2488
2489         case '1':
2490         case '2':
2491         case '3':
2492         case '4':
2493         case '5':
2494         case '6':
2495         case '7':
2496         case '8':
2497             (void) mvaddstr(SLK_WORK, 0, "Please enter the label value: ");
2498             strcpy(buf, "");
2499             if ((s = slk_label(c - '0')) != 0) {
2500                 strncpy(buf, s, 8);
2501             }
2502             wGetstring(stdscr, buf, 8);
2503             slk_set((c - '0'), buf, fmt);
2504             slk_refresh();
2505             move(SLK_WORK, 0);
2506             clrtobot();
2507             break;
2508
2509           case_QUIT:
2510             goto done;
2511
2512 #if HAVE_SLK_COLOR
2513         case 'F':
2514             if (use_colors) {
2515                 fg = (fg + 1) % COLORS;
2516                 call_slk_color(fg, bg);
2517             }
2518             break;
2519         case 'B':
2520             if (use_colors) {
2521                 bg = (bg + 1) % COLORS;
2522                 call_slk_color(fg, bg);
2523             }
2524             break;
2525 #endif
2526 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
2527         case KEY_RESIZE:
2528             wnoutrefresh(stdscr);
2529             break;
2530 #endif
2531
2532         default:
2533             beep();
2534         }
2535     } while (!isQuit(c = Getchar()));
2536
2537   done:
2538     slk_clear();
2539     erase();
2540     endwin();
2541 }
2542
2543 #if USE_WIDEC_SUPPORT
2544 #define SLKLEN 8
2545 static void
2546 wide_slk_test(void)
2547 /* exercise the soft keys */
2548 {
2549     int c, fmt = 1;
2550     wchar_t buf[SLKLEN + 1];
2551     char *s;
2552     short fg = COLOR_BLACK;
2553     short bg = COLOR_WHITE;
2554
2555     c = CTRL('l');
2556     if (use_colors) {
2557         call_slk_color(fg, bg);
2558     }
2559     do {
2560         move(0, 0);
2561         switch (c) {
2562         case CTRL('l'):
2563             erase();
2564             attr_on(WA_BOLD, NULL);
2565             mvaddstr(0, 20, "Soft Key Exerciser");
2566             attr_off(WA_BOLD, NULL);
2567
2568             slk_help();
2569             /* fall through */
2570
2571         case 'a':
2572             slk_restore();
2573             break;
2574
2575         case 'e':
2576             wclear(stdscr);
2577             break;
2578
2579         case 's':
2580             mvprintw(SLK_WORK, 0, "Press Q to stop the scrolling-test: ");
2581             while ((c = Getchar()) != 'Q' && (c != ERR))
2582                 addch((chtype) c);
2583             break;
2584
2585         case 'd':
2586             slk_clear();
2587             break;
2588
2589         case 'l':
2590             fmt = 0;
2591             break;
2592
2593         case 'c':
2594             fmt = 1;
2595             break;
2596
2597         case 'r':
2598             fmt = 2;
2599             break;
2600
2601         case '1':
2602         case '2':
2603         case '3':
2604         case '4':
2605         case '5':
2606         case '6':
2607         case '7':
2608         case '8':
2609             (void) mvaddstr(SLK_WORK, 0, "Please enter the label value: ");
2610             *buf = 0;
2611             if ((s = slk_label(c - '0')) != 0) {
2612                 char *temp = strdup(s);
2613                 size_t used = strlen(temp);
2614                 size_t want = SLKLEN;
2615                 size_t test;
2616                 mbstate_t state;
2617
2618                 buf[0] = L'\0';
2619                 while (want > 0 && used != 0) {
2620                     const char *base = s;
2621                     memset(&state, 0, sizeof(state));
2622                     test = mbsrtowcs(0, &base, 0, &state);
2623                     if (test == (size_t) -1) {
2624                         temp[--used] = 0;
2625                     } else if (test > want) {
2626                         temp[--used] = 0;
2627                     } else {
2628                         memset(&state, 0, sizeof(state));
2629                         mbsrtowcs(buf, &base, want, &state);
2630                         break;
2631                     }
2632                 }
2633                 free(temp);
2634             }
2635             wGet_wstring(stdscr, buf, SLKLEN);
2636             slk_wset((c - '0'), buf, fmt);
2637             slk_refresh();
2638             move(SLK_WORK, 0);
2639             clrtobot();
2640             break;
2641
2642           case_QUIT:
2643             goto done;
2644
2645         case 'F':
2646             if (use_colors) {
2647                 fg = (fg + 1) % COLORS;
2648                 call_slk_color(fg, bg);
2649             }
2650             break;
2651         case 'B':
2652             if (use_colors) {
2653                 bg = (bg + 1) % COLORS;
2654                 call_slk_color(fg, bg);
2655             }
2656             break;
2657 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
2658         case KEY_RESIZE:
2659             wnoutrefresh(stdscr);
2660             break;
2661 #endif
2662         default:
2663             beep();
2664         }
2665     } while (!isQuit(c = Getchar()));
2666
2667   done:
2668     slk_clear();
2669     erase();
2670     endwin();
2671 }
2672 #endif
2673 #endif /* SLK_INIT */
2674
2675 /****************************************************************************
2676  *
2677  * Alternate character-set stuff
2678  *
2679  ****************************************************************************/
2680
2681 /* ISO 6429:  codes 0x80 to 0x9f may be control characters that cause the
2682  * terminal to perform functions.  The remaining codes can be graphic.
2683  */
2684 static void
2685 show_upper_chars(unsigned first)
2686 {
2687     bool C1 = (first == 128);
2688     unsigned code;
2689     unsigned last = first + 31;
2690     int reply;
2691
2692     erase();
2693     attron(A_BOLD);
2694     mvprintw(0, 20, "Display of %s Character Codes %d to %d",
2695              C1 ? "C1" : "GR", first, last);
2696     attroff(A_BOLD);
2697     refresh();
2698
2699     for (code = first; code <= last; code++) {
2700         int row = 2 + ((code - first) % 16);
2701         int col = ((code - first) / 16) * COLS / 2;
2702         char tmp[80];
2703         sprintf(tmp, "%3u (0x%x)", code, code);
2704         mvprintw(row, col, "%*s: ", COLS / 4, tmp);
2705         if (C1)
2706             nodelay(stdscr, TRUE);
2707         echochar(code);
2708         if (C1) {
2709             /* (yes, this _is_ crude) */
2710             while ((reply = Getchar()) != ERR) {
2711                 addch(UChar(reply));
2712                 napms(10);
2713             }
2714             nodelay(stdscr, FALSE);
2715         }
2716     }
2717 }
2718
2719 static void
2720 show_pc_chars(void)
2721 {
2722     unsigned code;
2723
2724     erase();
2725     attron(A_BOLD);
2726     mvprintw(0, 20, "Display of PC Character Codes");
2727     attroff(A_BOLD);
2728     refresh();
2729
2730     for (code = 0; code < 16; ++code) {
2731         mvprintw(2, (int) code * 3 + 8, "%X", code);
2732     }
2733     for (code = 0; code < 256; code++) {
2734         int row = 3 + (code / 16) + (code >= 128);
2735         int col = 8 + (code % 16) * 3;
2736         if ((code % 16) == 0)
2737             mvprintw(row, 0, "0x%02x:", code);
2738         move(row, col);
2739         switch (code) {
2740         case '\n':
2741         case '\r':
2742         case '\b':
2743         case '\f':
2744         case '\033':
2745         case 0x9b:
2746             /*
2747              * Skip the ones that do not work.
2748              */
2749             break;
2750         default:
2751             addch(code | A_ALTCHARSET);
2752             break;
2753         }
2754     }
2755 }
2756
2757 static void
2758 show_box_chars(void)
2759 {
2760     erase();
2761     attron(A_BOLD);
2762     mvaddstr(0, 20, "Display of the ACS Line-Drawing Set");
2763     attroff(A_BOLD);
2764     refresh();
2765     box(stdscr, 0, 0);
2766     /* *INDENT-OFF* */
2767     mvhline(LINES / 2, 0,        ACS_HLINE, COLS);
2768     mvvline(0,         COLS / 2, ACS_VLINE, LINES);
2769     mvaddch(0,         COLS / 2, ACS_TTEE);
2770     mvaddch(LINES / 2, COLS / 2, ACS_PLUS);
2771     mvaddch(LINES - 1, COLS / 2, ACS_BTEE);
2772     mvaddch(LINES / 2, 0,        ACS_LTEE);
2773     mvaddch(LINES / 2, COLS - 1, ACS_RTEE);
2774     /* *INDENT-ON* */
2775
2776 }
2777
2778 static int
2779 show_1_acs(int n, const char *name, chtype code)
2780 {
2781     const int height = 16;
2782     int row = 2 + (n % height);
2783     int col = (n / height) * COLS / 2;
2784     mvprintw(row, col, "%*s : ", COLS / 4, name);
2785     addch(code);
2786     return n + 1;
2787 }
2788
2789 static void
2790 show_acs_chars(void)
2791 /* display the ACS character set */
2792 {
2793     int n;
2794
2795 #define BOTH(name) #name, name
2796
2797     erase();
2798     attron(A_BOLD);
2799     mvaddstr(0, 20, "Display of the ACS Character Set");
2800     attroff(A_BOLD);
2801     refresh();
2802
2803     n = show_1_acs(0, BOTH(ACS_ULCORNER));
2804     n = show_1_acs(n, BOTH(ACS_URCORNER));
2805     n = show_1_acs(n, BOTH(ACS_LLCORNER));
2806     n = show_1_acs(n, BOTH(ACS_LRCORNER));
2807
2808     n = show_1_acs(n, BOTH(ACS_LTEE));
2809     n = show_1_acs(n, BOTH(ACS_RTEE));
2810     n = show_1_acs(n, BOTH(ACS_TTEE));
2811     n = show_1_acs(n, BOTH(ACS_BTEE));
2812
2813     n = show_1_acs(n, BOTH(ACS_HLINE));
2814     n = show_1_acs(n, BOTH(ACS_VLINE));
2815
2816     /*
2817      * HPUX's ACS definitions are broken here.  Just give up.
2818      */
2819 #if !(defined(__hpux) && !defined(NCURSES_VERSION))
2820     n = show_1_acs(n, BOTH(ACS_LARROW));
2821     n = show_1_acs(n, BOTH(ACS_RARROW));
2822     n = show_1_acs(n, BOTH(ACS_UARROW));
2823     n = show_1_acs(n, BOTH(ACS_DARROW));
2824
2825     n = show_1_acs(n, BOTH(ACS_BLOCK));
2826     n = show_1_acs(n, BOTH(ACS_BOARD));
2827     n = show_1_acs(n, BOTH(ACS_LANTERN));
2828     n = show_1_acs(n, BOTH(ACS_BULLET));
2829     n = show_1_acs(n, BOTH(ACS_CKBOARD));
2830     n = show_1_acs(n, BOTH(ACS_DEGREE));
2831     n = show_1_acs(n, BOTH(ACS_DIAMOND));
2832     n = show_1_acs(n, BOTH(ACS_PLMINUS));
2833     n = show_1_acs(n, BOTH(ACS_PLUS));
2834
2835     n = show_1_acs(n, BOTH(ACS_GEQUAL));
2836     n = show_1_acs(n, BOTH(ACS_NEQUAL));
2837     n = show_1_acs(n, BOTH(ACS_LEQUAL));
2838
2839     n = show_1_acs(n, BOTH(ACS_STERLING));
2840     n = show_1_acs(n, BOTH(ACS_PI));
2841     n = show_1_acs(n, BOTH(ACS_S1));
2842     n = show_1_acs(n, BOTH(ACS_S3));
2843     n = show_1_acs(n, BOTH(ACS_S7));
2844     n = show_1_acs(n, BOTH(ACS_S9));
2845 #endif
2846 }
2847
2848 static void
2849 acs_display(void)
2850 {
2851     int c = 'a';
2852     char *term = getenv("TERM");
2853     const char *pch_kludge = ((term != 0 && strstr(term, "linux"))
2854                               ? "p=PC, "
2855                               : "");
2856
2857     do {
2858         switch (c) {
2859         case CTRL('L'):
2860             Repaint();
2861             break;
2862         case 'a':
2863             show_acs_chars();
2864             break;
2865         case 'x':
2866             show_box_chars();
2867             break;
2868         case '0':
2869         case '1':
2870         case '2':
2871         case '3':
2872             show_upper_chars((unsigned) ((c - '0') * 32 + 128));
2873             break;
2874         case 'p':
2875             show_pc_chars();
2876             break;
2877         default:
2878             beep();
2879             break;
2880         }
2881         mvprintw(LINES - 3, 0,
2882                  "Note: ANSI terminals may not display C1 characters.");
2883         mvprintw(LINES - 2, 0,
2884                  "Select: a=ACS, x=box, %s0=C1, 1,2,3=GR characters, ESC=quit",
2885                  pch_kludge);
2886         refresh();
2887     } while (!isQuit(c = Getchar()));
2888
2889     Pause();
2890     erase();
2891     endwin();
2892 }
2893
2894 #if USE_WIDEC_SUPPORT
2895 static cchar_t *
2896 merge_wide_attr(cchar_t *dst, const cchar_t *src, attr_t attr, short pair)
2897 {
2898     int count = getcchar(src, NULL, NULL, NULL, 0);
2899     wchar_t *wch = 0;
2900     attr_t ignore_attr;
2901     short ignore_pair;
2902
2903     *dst = *src;
2904     if (count > 0) {
2905         if ((wch = typeMalloc(wchar_t, count)) != 0) {
2906             if (getcchar(src, wch, &ignore_attr, &ignore_pair, 0) != ERR) {
2907                 attr |= (ignore_attr & A_ALTCHARSET);
2908                 setcchar(dst, wch, attr, pair, 0);
2909             }
2910             free(wch);
2911         }
2912     }
2913     return dst;
2914 }
2915
2916 static void
2917 show_upper_widechars(int first, int repeat, int space, attr_t attr, short pair)
2918 {
2919     cchar_t temp;
2920     wchar_t code;
2921     int last = first + 31;
2922
2923     erase();
2924     attron(A_BOLD);
2925     mvprintw(0, 20, "Display of Character Codes %d to %d", first, last);
2926     attroff(A_BOLD);
2927
2928     for (code = first; code <= last; code++) {
2929         int row = 2 + ((code - first) % 16);
2930         int col = ((code - first) / 16) * COLS / 2;
2931         wchar_t codes[10];
2932         char tmp[80];
2933         int count = repeat;
2934         int y, x;
2935
2936         memset(&codes, 0, sizeof(codes));
2937         codes[0] = code;
2938         sprintf(tmp, "%3ld (0x%lx)", (long) code, (long) code);
2939         mvprintw(row, col, "%*s: ", COLS / 4, tmp);
2940         setcchar(&temp, codes, attr, pair, 0);
2941         do {
2942             /*
2943              * Give non-spacing characters something to combine with.  If we
2944              * don't, they'll bunch up in a heap on the space after the ":".
2945              * Mark them with reverse-video to make them simpler to find on
2946              * the display.
2947              */
2948             if (wcwidth(code) == 0)
2949                 addch(space | A_REVERSE);
2950             /*
2951              * This could use add_wch(), but is done for comparison with the
2952              * normal 'f' test (and to make a test-case for echo_wchar()).
2953              * The screen will flicker because the erase() at the top of the
2954              * function is met by the builtin refresh() in echo_wchar().
2955              */
2956             echo_wchar(&temp);
2957             /*
2958              * The repeat-count may make text wrap - avoid that.
2959              */
2960             getyx(stdscr, y, x);
2961             if (x >= col + (COLS / 2) - 2)
2962                 break;
2963         } while (--count > 0);
2964     }
2965 }
2966
2967 static int
2968 show_1_wacs(int n, const char *name, const cchar_t *code)
2969 {
2970     const int height = 16;
2971     int row = 2 + (n % height);
2972     int col = (n / height) * COLS / 2;
2973     mvprintw(row, col, "%*s : ", COLS / 4, name);
2974     add_wchnstr(code, 1);
2975     return n + 1;
2976 }
2977
2978 #define MERGE_ATTR(wch) merge_wide_attr(&temp, wch, attr, pair)
2979
2980 static void
2981 show_wacs_chars(attr_t attr, short pair)
2982 /* display the wide-ACS character set */
2983 {
2984     cchar_t temp;
2985
2986     int n;
2987
2988 /*#define BOTH2(name) #name, &(name) */
2989 #define BOTH2(name) #name, MERGE_ATTR(name)
2990
2991     erase();
2992     attron(A_BOLD);
2993     mvaddstr(0, 20, "Display of the Wide-ACS Character Set");
2994     attroff(A_BOLD);
2995     refresh();
2996
2997     n = show_1_wacs(0, BOTH2(WACS_ULCORNER));
2998     n = show_1_wacs(n, BOTH2(WACS_URCORNER));
2999     n = show_1_wacs(n, BOTH2(WACS_LLCORNER));
3000     n = show_1_wacs(n, BOTH2(WACS_LRCORNER));
3001
3002     n = show_1_wacs(n, BOTH2(WACS_LTEE));
3003     n = show_1_wacs(n, BOTH2(WACS_RTEE));
3004     n = show_1_wacs(n, BOTH2(WACS_TTEE));
3005     n = show_1_wacs(n, BOTH2(WACS_BTEE));
3006
3007     n = show_1_wacs(n, BOTH2(WACS_HLINE));
3008     n = show_1_wacs(n, BOTH2(WACS_VLINE));
3009
3010     n = show_1_wacs(n, BOTH2(WACS_LARROW));
3011     n = show_1_wacs(n, BOTH2(WACS_RARROW));
3012     n = show_1_wacs(n, BOTH2(WACS_UARROW));
3013     n = show_1_wacs(n, BOTH2(WACS_DARROW));
3014
3015     n = show_1_wacs(n, BOTH2(WACS_BLOCK));
3016     n = show_1_wacs(n, BOTH2(WACS_BOARD));
3017     n = show_1_wacs(n, BOTH2(WACS_LANTERN));
3018     n = show_1_wacs(n, BOTH2(WACS_BULLET));
3019     n = show_1_wacs(n, BOTH2(WACS_CKBOARD));
3020     n = show_1_wacs(n, BOTH2(WACS_DEGREE));
3021     n = show_1_wacs(n, BOTH2(WACS_DIAMOND));
3022     n = show_1_wacs(n, BOTH2(WACS_PLMINUS));
3023     n = show_1_wacs(n, BOTH2(WACS_PLUS));
3024
3025 #ifdef CURSES_WACS_ARRAY
3026     n = show_1_wacs(n, BOTH2(WACS_GEQUAL));
3027     n = show_1_wacs(n, BOTH2(WACS_NEQUAL));
3028     n = show_1_wacs(n, BOTH2(WACS_LEQUAL));
3029
3030     n = show_1_wacs(n, BOTH2(WACS_STERLING));
3031     n = show_1_wacs(n, BOTH2(WACS_PI));
3032     n = show_1_wacs(n, BOTH2(WACS_S1));
3033     n = show_1_wacs(n, BOTH2(WACS_S3));
3034     n = show_1_wacs(n, BOTH2(WACS_S7));
3035     n = show_1_wacs(n, BOTH2(WACS_S9));
3036 #endif
3037 }
3038
3039 #undef MERGE_ATTR
3040
3041 #define MERGE_ATTR(wch) merge_wide_attr(&temp, wch, attr, pair)
3042
3043 static void
3044 show_wbox_chars(attr_t attr, short pair)
3045 {
3046     cchar_t temp;
3047
3048     erase();
3049     attron(A_BOLD);
3050     mvaddstr(0, 20, "Display of the Wide-ACS Line-Drawing Set");
3051     attroff(A_BOLD);
3052     refresh();
3053
3054     attr_set(attr, pair, 0);
3055     box_set(stdscr, 0, 0);
3056     attr_set(A_NORMAL, 0, 0);
3057     /* *INDENT-OFF* */
3058     mvhline_set(LINES / 2, 0,        MERGE_ATTR(WACS_HLINE), COLS);
3059     mvvline_set(0,         COLS / 2, MERGE_ATTR(WACS_VLINE), LINES);
3060     mvadd_wch(0,           COLS / 2, MERGE_ATTR(WACS_TTEE));
3061     mvadd_wch(LINES / 2,   COLS / 2, MERGE_ATTR(WACS_PLUS));
3062     mvadd_wch(LINES - 1,   COLS / 2, MERGE_ATTR(WACS_BTEE));
3063     mvadd_wch(LINES / 2,   0,        MERGE_ATTR(WACS_LTEE));
3064     mvadd_wch(LINES / 2,   COLS - 1, MERGE_ATTR(WACS_RTEE));
3065     /* *INDENT-ON* */
3066
3067 }
3068
3069 #undef MERGE_ATTR
3070
3071 static int
3072 show_2_wacs(int n, const char *name, const char *code, attr_t attr, short pair)
3073 {
3074     const int height = 16;
3075     int row = 2 + (n % height);
3076     int col = (n / height) * COLS / 2;
3077     char temp[80];
3078
3079     mvprintw(row, col, "%*s : ", COLS / 4, name);
3080     attr_set(attr, pair, 0);
3081     addstr(strcpy(temp, code));
3082     attr_set(A_NORMAL, 0, 0);
3083     return n + 1;
3084 }
3085
3086 #define SHOW_UTF8(n, name, code) show_2_wacs(n, name, code, attr, pair)
3087
3088 static void
3089 show_utf8_chars(attr_t attr, short pair)
3090 {
3091     int n;
3092
3093     erase();
3094     attron(A_BOLD);
3095     mvaddstr(0, 20, "Display of the Wide-ACS Character Set");
3096     attroff(A_BOLD);
3097     refresh();
3098     /* *INDENT-OFF* */
3099     n = SHOW_UTF8(0, "WACS_ULCORNER",   "\342\224\214");
3100     n = SHOW_UTF8(n, "WACS_URCORNER",   "\342\224\220");
3101     n = SHOW_UTF8(n, "WACS_LLCORNER",   "\342\224\224");
3102     n = SHOW_UTF8(n, "WACS_LRCORNER",   "\342\224\230");
3103
3104     n = SHOW_UTF8(n, "WACS_LTEE",       "\342\224\234");
3105     n = SHOW_UTF8(n, "WACS_RTEE",       "\342\224\244");
3106     n = SHOW_UTF8(n, "WACS_TTEE",       "\342\224\254");
3107     n = SHOW_UTF8(n, "WACS_BTEE",       "\342\224\264");
3108
3109     n = SHOW_UTF8(n, "WACS_HLINE",      "\342\224\200");
3110     n = SHOW_UTF8(n, "WACS_VLINE",      "\342\224\202");
3111
3112     n = SHOW_UTF8(n, "WACS_LARROW",     "\342\206\220");
3113     n = SHOW_UTF8(n, "WACS_RARROW",     "\342\206\222");
3114     n = SHOW_UTF8(n, "WACS_UARROW",     "\342\206\221");
3115     n = SHOW_UTF8(n, "WACS_DARROW",     "\342\206\223");
3116
3117     n = SHOW_UTF8(n, "WACS_BLOCK",      "\342\226\256");
3118     n = SHOW_UTF8(n, "WACS_BOARD",      "\342\226\222");
3119     n = SHOW_UTF8(n, "WACS_LANTERN",    "\342\230\203");
3120     n = SHOW_UTF8(n, "WACS_BULLET",     "\302\267");
3121     n = SHOW_UTF8(n, "WACS_CKBOARD",    "\342\226\222");
3122     n = SHOW_UTF8(n, "WACS_DEGREE",     "\302\260");
3123     n = SHOW_UTF8(n, "WACS_DIAMOND",    "\342\227\206");
3124     n = SHOW_UTF8(n, "WACS_PLMINUS",    "\302\261");
3125     n = SHOW_UTF8(n, "WACS_PLUS",       "\342\224\274");
3126     n = SHOW_UTF8(n, "WACS_GEQUAL",     "\342\211\245");
3127     n = SHOW_UTF8(n, "WACS_NEQUAL",     "\342\211\240");
3128     n = SHOW_UTF8(n, "WACS_LEQUAL",     "\342\211\244");
3129
3130     n = SHOW_UTF8(n, "WACS_STERLING",   "\302\243");
3131     n = SHOW_UTF8(n, "WACS_PI",         "\317\200");
3132     n = SHOW_UTF8(n, "WACS_S1",         "\342\216\272");
3133     n = SHOW_UTF8(n, "WACS_S3",         "\342\216\273");
3134     n = SHOW_UTF8(n, "WACS_S7",         "\342\216\274");
3135     n = SHOW_UTF8(n, "WACS_S9",         "\342\216\275");
3136     /* *INDENT-ON* */
3137
3138 }
3139 /* *INDENT-OFF* */
3140 static struct {
3141     chtype attr;
3142     const char *name;
3143 } attrs_to_cycle[] = {
3144     { A_NORMAL,         "normal" },
3145     { A_BOLD,           "bold" },
3146     { A_REVERSE,        "reverse" },
3147     { A_UNDERLINE,      "underline" },
3148 };
3149 /* *INDENT-ON* */
3150
3151 static bool
3152 cycle_attr(int ch, unsigned *at_code, chtype *attr)
3153 {
3154     bool result = TRUE;
3155
3156     switch (ch) {
3157     case 'v':
3158         if ((*at_code += 1) >= SIZEOF(attrs_to_cycle))
3159             *at_code = 0;
3160         break;
3161     case 'V':
3162         if (*at_code == 1)
3163             *at_code = SIZEOF(attrs_to_cycle) - 1;
3164         else
3165             *at_code -= 1;
3166         break;
3167     default:
3168         result = FALSE;
3169         break;
3170     }
3171     if (result)
3172         *attr = attrs_to_cycle[*at_code].attr;
3173     return result;
3174 }
3175
3176 static bool
3177 cycle_colors(int ch, int *fg, int *bg, short *pair)
3178 {
3179     bool result = FALSE;
3180
3181     if (use_colors) {
3182         result = TRUE;
3183         switch (ch) {
3184         case 'F':
3185             if ((*fg -= 1) < 0)
3186                 *fg = COLORS - 1;
3187             break;
3188         case 'f':
3189             if ((*fg += 1) >= COLORS)
3190                 *fg = 0;
3191             break;
3192         case 'B':
3193             if ((*bg -= 1) < 0)
3194                 *bg = COLORS - 1;
3195             break;
3196         case 'b':
3197             if ((*bg += 1) < 0)
3198                 *bg = 0;
3199             break;
3200         default:
3201             result = FALSE;
3202             break;
3203         }
3204         if (result) {
3205             *pair = (*fg != COLOR_BLACK || *bg != COLOR_BLACK);
3206             if (*pair != 0) {
3207                 *pair = 1;
3208                 if (init_pair(*pair, *fg, *bg) == ERR) {
3209                     result = FALSE;
3210                 }
3211             }
3212         }
3213     }
3214     return result;
3215 }
3216
3217 /* display the wide-ACS character set */
3218 static void
3219 wide_acs_display(void)
3220 {
3221     int c = 'a';
3222     int digit = 0;
3223     int repeat = 0;
3224     int space = ' ';
3225     chtype attr = A_NORMAL;
3226     int fg = COLOR_BLACK;
3227     int bg = COLOR_BLACK;
3228     unsigned at_code = 0;
3229     short pair = 0;
3230     void (*last_show_wacs) (attr_t, short) = 0;
3231
3232     do {
3233         switch (c) {
3234         case CTRL('L'):
3235             Repaint();
3236             break;
3237         case 'a':
3238             last_show_wacs = show_wacs_chars;
3239             break;
3240         case 'x':
3241             last_show_wacs = show_wbox_chars;
3242             break;
3243         case 'u':
3244             last_show_wacs = show_utf8_chars;
3245             break;
3246         default:
3247             if (c < 256 && isdigit(c)) {
3248                 digit = (c - '0');
3249             } else if (c == '+') {
3250                 ++digit;
3251             } else if (c == '-' && digit > 0) {
3252                 --digit;
3253             } else if (c == '>' && repeat < (COLS / 4)) {
3254                 ++repeat;
3255             } else if (c == '<' && repeat > 0) {
3256                 --repeat;
3257             } else if (c == '_') {
3258                 space = (space == ' ') ? '_' : ' ';
3259             } else if (cycle_attr(c, &at_code, &attr)
3260                        || cycle_colors(c, &fg, &bg, &pair)) {
3261                 if (last_show_wacs != 0)
3262                     break;
3263             } else {
3264                 beep();
3265                 break;
3266             }
3267             last_show_wacs = 0;
3268             show_upper_widechars(digit * 32 + 128, repeat, space, attr, pair);
3269             break;
3270         }
3271         if (last_show_wacs != 0)
3272             last_show_wacs(attr, pair);
3273
3274         mvprintw(LINES - 3, 0,
3275                  "Select: a WACS, x box, u UTF-8, 0-9,+/- non-ASCII, </> repeat, ESC=quit");
3276         if (use_colors) {
3277             mvprintw(LINES - 2, 0,
3278                      "v/V, f/F, b/B cycle through video attributes (%s) and color %d/%d.",
3279                      attrs_to_cycle[at_code].name,
3280                      fg, bg);
3281         } else {
3282             mvprintw(LINES - 2, 0,
3283                      "v/V cycles through video attributes (%s).",
3284                      attrs_to_cycle[at_code].name);
3285         }
3286         refresh();
3287     } while (!isQuit(c = Getchar()));
3288
3289     Pause();
3290     erase();
3291     endwin();
3292 }
3293
3294 #endif
3295
3296 /*
3297  * Graphic-rendition test (adapted from vttest)
3298  */
3299 static void
3300 test_sgr_attributes(void)
3301 {
3302     int pass;
3303
3304     for (pass = 0; pass < 2; pass++) {
3305         chtype normal = ((pass == 0 ? A_NORMAL : A_REVERSE)) | BLANK;
3306
3307         /* Use non-default colors if possible to exercise bce a little */
3308         if (use_colors) {
3309             init_pair(1, COLOR_WHITE, COLOR_BLUE);
3310             normal |= COLOR_PAIR(1);
3311         }
3312         bkgdset(normal);
3313         erase();
3314         mvprintw(1, 20, "Graphic rendition test pattern:");
3315
3316         mvprintw(4, 1, "vanilla");
3317
3318 #define set_sgr(mask) bkgdset((normal^(mask)));
3319         set_sgr(A_BOLD);
3320         mvprintw(4, 40, "bold");
3321
3322         set_sgr(A_UNDERLINE);
3323         mvprintw(6, 6, "underline");
3324
3325         set_sgr(A_BOLD | A_UNDERLINE);
3326         mvprintw(6, 45, "bold underline");
3327
3328         set_sgr(A_BLINK);
3329         mvprintw(8, 1, "blink");
3330
3331         set_sgr(A_BLINK | A_BOLD);
3332         mvprintw(8, 40, "bold blink");
3333
3334         set_sgr(A_UNDERLINE | A_BLINK);
3335         mvprintw(10, 6, "underline blink");
3336
3337         set_sgr(A_BOLD | A_UNDERLINE | A_BLINK);
3338         mvprintw(10, 45, "bold underline blink");
3339
3340         set_sgr(A_REVERSE);
3341         mvprintw(12, 1, "negative");
3342
3343         set_sgr(A_BOLD | A_REVERSE);
3344         mvprintw(12, 40, "bold negative");
3345
3346         set_sgr(A_UNDERLINE | A_REVERSE);
3347         mvprintw(14, 6, "underline negative");
3348
3349         set_sgr(A_BOLD | A_UNDERLINE | A_REVERSE);
3350         mvprintw(14, 45, "bold underline negative");
3351
3352         set_sgr(A_BLINK | A_REVERSE);
3353         mvprintw(16, 1, "blink negative");
3354
3355         set_sgr(A_BOLD | A_BLINK | A_REVERSE);
3356         mvprintw(16, 40, "bold blink negative");
3357
3358         set_sgr(A_UNDERLINE | A_BLINK | A_REVERSE);
3359         mvprintw(18, 6, "underline blink negative");
3360
3361         set_sgr(A_BOLD | A_UNDERLINE | A_BLINK | A_REVERSE);
3362         mvprintw(18, 45, "bold underline blink negative");
3363
3364         bkgdset(normal);
3365         mvprintw(LINES - 2, 1, "%s background. ", pass == 0 ? "Dark" :
3366                  "Light");
3367         clrtoeol();
3368         Pause();
3369     }
3370
3371     bkgdset(A_NORMAL | BLANK);
3372     erase();
3373     endwin();
3374 }
3375
3376 /****************************************************************************
3377  *
3378  * Windows and scrolling tester.
3379  *
3380  ****************************************************************************/
3381
3382 #define BOTLINES        4       /* number of line stolen from screen bottom */
3383
3384 typedef struct {
3385     int y, x;
3386 } pair;
3387
3388 #define FRAME struct frame
3389 FRAME
3390 {
3391     FRAME *next, *last;
3392     bool do_scroll;
3393     bool do_keypad;
3394     WINDOW *wind;
3395 };
3396
3397 #if defined(NCURSES_VERSION)
3398 #if (NCURSES_VERSION_PATCH < 20070331) && NCURSES_EXT_FUNCS
3399 #define is_keypad(win)   (win)->_use_keypad
3400 #define is_scrollok(win) (win)->_scroll
3401 #elif !defined(is_keypad)
3402 #define is_keypad(win)   FALSE
3403 #define is_scrollok(win) FALSE
3404 #endif
3405 #else
3406 #define is_keypad(win)   FALSE
3407 #define is_scrollok(win) FALSE
3408 #endif
3409
3410 /* We need to know if these flags are actually set, so don't look in FRAME.
3411  * These names are known to work with SVr4 curses as well as ncurses.  The
3412  * _use_keypad name does not work with Solaris 8.
3413  */
3414 static bool
3415 HaveKeypad(FRAME * curp)
3416 {
3417     WINDOW *win = (curp ? curp->wind : stdscr);
3418     (void) win;
3419     return is_keypad(win);
3420 }
3421
3422 static bool
3423 HaveScroll(FRAME * curp)
3424 {
3425     WINDOW *win = (curp ? curp->wind : stdscr);
3426     (void) win;
3427     return is_scrollok(win);
3428 }
3429
3430 static void
3431 newwin_legend(FRAME * curp)
3432 {
3433     static const struct {
3434         const char *msg;
3435         int code;
3436     } legend[] = {
3437         {
3438             "^C = create window", 0
3439         },
3440         {
3441             "^N = next window", 0
3442         },
3443         {
3444             "^P = previous window", 0
3445         },
3446         {
3447             "^F = scroll forward", 0
3448         },
3449         {
3450             "^B = scroll backward", 0
3451         },
3452         {
3453             "^K = keypad(%s)", 1
3454         },
3455         {
3456             "^S = scrollok(%s)", 2
3457         },
3458         {
3459             "^W = save window to file", 0
3460         },
3461         {
3462             "^R = restore window", 0
3463         },
3464 #if HAVE_WRESIZE
3465         {
3466             "^X = resize", 0
3467         },
3468 #endif
3469         {
3470             "^Q%s = exit", 3
3471         }
3472     };
3473     size_t n;
3474     int x;
3475     bool do_keypad = HaveKeypad(curp);
3476     bool do_scroll = HaveScroll(curp);
3477     char buf[BUFSIZ];
3478
3479     move(LINES - 4, 0);
3480     for (n = 0; n < SIZEOF(legend); n++) {
3481         switch (legend[n].code) {
3482         default:
3483             strcpy(buf, legend[n].msg);
3484             break;
3485         case 1:
3486             sprintf(buf, legend[n].msg, do_keypad ? "yes" : "no");
3487             break;
3488         case 2:
3489             sprintf(buf, legend[n].msg, do_scroll ? "yes" : "no");
3490             break;
3491         case 3:
3492             sprintf(buf, legend[n].msg, do_keypad ? "/ESC" : "");
3493             break;
3494         }
3495         x = getcurx(stdscr);
3496         addstr((COLS < (x + 3 + (int) strlen(buf))) ? "\n" : (n ? ", " : ""));
3497         addstr(buf);
3498     }
3499     clrtoeol();
3500 }
3501
3502 static void
3503 transient(FRAME * curp, NCURSES_CONST char *msg)
3504 {
3505     newwin_legend(curp);
3506     if (msg) {
3507         mvaddstr(LINES - 1, 0, msg);
3508         refresh();
3509         napms(1000);
3510     }
3511
3512     move(LINES - 1, 0);
3513     printw("%s characters are echoed, window should %sscroll.",
3514            HaveKeypad(curp) ? "Non-arrow" : "All other",
3515            HaveScroll(curp) ? "" : "not ");
3516     clrtoeol();
3517 }
3518
3519 static void
3520 newwin_report(FRAME * curp)
3521 /* report on the cursor's current position, then restore it */
3522 {
3523     WINDOW *win = (curp != 0) ? curp->wind : stdscr;
3524     int y, x;
3525
3526     if (win != stdscr)
3527         transient(curp, (char *) 0);
3528     getyx(win, y, x);
3529     move(LINES - 1, COLS - 17);
3530     printw("Y = %2d X = %2d", y, x);
3531     if (win != stdscr)
3532         refresh();
3533     else
3534         wmove(win, y, x);
3535 }
3536
3537 static pair *
3538 selectcell(int uli, int ulj, int lri, int lrj)
3539 /* arrows keys move cursor, return location at current on non-arrow key */
3540 {
3541     static pair res;            /* result cell */
3542     int si = lri - uli + 1;     /* depth of the select area */
3543     int sj = lrj - ulj + 1;     /* width of the select area */
3544     int i = 0, j = 0;           /* offsets into the select area */
3545
3546     res.y = uli;
3547     res.x = ulj;
3548     for (;;) {
3549         move(uli + i, ulj + j);
3550         newwin_report((FRAME *) 0);
3551
3552         switch (Getchar()) {
3553         case KEY_UP:
3554             i += si - 1;
3555             break;
3556         case KEY_DOWN:
3557             i++;
3558             break;
3559         case KEY_LEFT:
3560             j += sj - 1;
3561             break;
3562         case KEY_RIGHT:
3563             j++;
3564             break;
3565           case_QUIT:
3566             return ((pair *) 0);
3567 #ifdef NCURSES_MOUSE_VERSION
3568         case KEY_MOUSE:
3569             {
3570                 MEVENT event;
3571
3572                 getmouse(&event);
3573                 if (event.y > uli && event.x > ulj) {
3574                     i = event.y - uli;
3575                     j = event.x - ulj;
3576                 } else {
3577                     beep();
3578                     break;
3579                 }
3580             }
3581             /* FALLTHRU */
3582 #endif
3583         default:
3584             res.y = uli + i;
3585             res.x = ulj + j;
3586             return (&res);
3587         }
3588         i %= si;
3589         j %= sj;
3590     }
3591 }
3592
3593 static void
3594 outerbox(pair ul, pair lr, bool onoff)
3595 /* draw or erase a box *outside* the given pair of corners */
3596 {
3597     mvaddch(ul.y - 1, lr.x - 1, onoff ? ACS_ULCORNER : ' ');
3598     mvaddch(ul.y - 1, lr.x + 1, onoff ? ACS_URCORNER : ' ');
3599     mvaddch(lr.y + 1, lr.x + 1, onoff ? ACS_LRCORNER : ' ');
3600     mvaddch(lr.y + 1, ul.x - 1, onoff ? ACS_LLCORNER : ' ');
3601     move(ul.y - 1, ul.x);
3602     hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
3603     move(ul.y, ul.x - 1);
3604     vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
3605     move(lr.y + 1, ul.x);
3606     hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
3607     move(ul.y, lr.x + 1);
3608     vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
3609 }
3610
3611 static WINDOW *
3612 getwindow(void)
3613 /* Ask user for a window definition */
3614 {
3615     WINDOW *rwindow;
3616     pair ul, lr, *tmp;
3617
3618     move(0, 0);
3619     clrtoeol();
3620     addstr("Use arrows to move cursor, anything else to mark corner 1");
3621     refresh();
3622     if ((tmp = selectcell(2, 1, LINES - BOTLINES - 2, COLS - 2)) == (pair *) 0)
3623         return ((WINDOW *) 0);
3624     memcpy(&ul, tmp, sizeof(pair));
3625     mvaddch(ul.y - 1, ul.x - 1, ACS_ULCORNER);
3626     move(0, 0);
3627     clrtoeol();
3628     addstr("Use arrows to move cursor, anything else to mark corner 2");
3629     refresh();
3630     if ((tmp = selectcell(ul.y, ul.x, LINES - BOTLINES - 2, COLS - 2)) ==
3631         (pair *) 0)
3632         return ((WINDOW *) 0);
3633     memcpy(&lr, tmp, sizeof(pair));
3634
3635     rwindow = subwin(stdscr, lr.y - ul.y + 1, lr.x - ul.x + 1, ul.y, ul.x);
3636
3637     outerbox(ul, lr, TRUE);
3638     refresh();
3639
3640     wrefresh(rwindow);
3641
3642     move(0, 0);
3643     clrtoeol();
3644     return (rwindow);
3645 }
3646
3647 static void
3648 newwin_move(FRAME * curp, int dy, int dx)
3649 {
3650     WINDOW *win = (curp != 0) ? curp->wind : stdscr;
3651     int cur_y, cur_x;
3652     int max_y, max_x;
3653
3654     getyx(win, cur_y, cur_x);
3655     getmaxyx(win, max_y, max_x);
3656     if ((cur_x += dx) < 0)
3657         cur_x = 0;
3658     else if (cur_x >= max_x)
3659         cur_x = max_x - 1;
3660     if ((cur_y += dy) < 0)
3661         cur_y = 0;
3662     else if (cur_y >= max_y)
3663         cur_y = max_y - 1;
3664     wmove(win, cur_y, cur_x);
3665 }
3666
3667 static FRAME *
3668 delete_framed(FRAME * fp, bool showit)
3669 {
3670     FRAME *np;
3671
3672     fp->last->next = fp->next;
3673     fp->next->last = fp->last;
3674
3675     if (showit) {
3676         werase(fp->wind);
3677         wrefresh(fp->wind);
3678     }
3679     delwin(fp->wind);
3680
3681     np = (fp == fp->next) ? 0 : fp->next;
3682     free(fp);
3683     return np;
3684 }
3685
3686 static void
3687 acs_and_scroll(void)
3688 /* Demonstrate windows */
3689 {
3690     int c;
3691     FRAME *current = (FRAME *) 0, *neww;
3692     WINDOW *usescr = stdscr;
3693 #if HAVE_PUTWIN && HAVE_GETWIN
3694     FILE *fp;
3695 #endif
3696
3697 #define DUMPFILE        "screendump"
3698
3699 #ifdef NCURSES_MOUSE_VERSION
3700     mousemask(BUTTON1_CLICKED, (mmask_t *) 0);
3701 #endif
3702     c = CTRL('C');
3703     raw();
3704     do {
3705         transient((FRAME *) 0, (char *) 0);
3706         switch (c) {
3707         case CTRL('C'):
3708             neww = (FRAME *) calloc(1, sizeof(FRAME));
3709             if ((neww->wind = getwindow()) == (WINDOW *) 0)
3710                 goto breakout;
3711
3712             if (current == 0) { /* First element,  */
3713                 neww->next = neww;      /*   so point it at itself */
3714                 neww->last = neww;
3715             } else {
3716                 neww->next = current->next;
3717                 neww->last = current;
3718                 neww->last->next = neww;
3719                 neww->next->last = neww;
3720             }
3721             current = neww;
3722             /* SVr4 curses sets the keypad on all newly-created windows to
3723              * false.  Someone reported that PDCurses makes new windows inherit
3724              * this flag.  Remove the following 'keypad()' call to test this
3725              */
3726             keypad(current->wind, TRUE);
3727             current->do_keypad = HaveKeypad(current);
3728             current->do_scroll = HaveScroll(current);
3729             break;
3730
3731         case CTRL('N'): /* go to next window */
3732             if (current)
3733                 current = current->next;
3734             break;
3735
3736         case CTRL('P'): /* go to previous window */
3737             if (current)
3738                 current = current->last;
3739             break;
3740
3741         case CTRL('F'): /* scroll current window forward */
3742             if (current)
3743                 wscrl(current->wind, 1);
3744             break;
3745
3746         case CTRL('B'): /* scroll current window backwards */
3747             if (current)
3748                 wscrl(current->wind, -1);
3749             break;
3750
3751         case CTRL('K'): /* toggle keypad mode for current */
3752             if (current) {
3753                 current->do_keypad = !current->do_keypad;
3754                 keypad(current->wind, current->do_keypad);
3755             }
3756             break;
3757
3758         case CTRL('S'):
3759             if (current) {
3760                 current->do_scroll = !current->do_scroll;
3761                 scrollok(current->wind, current->do_scroll);
3762             }
3763             break;
3764
3765 #if HAVE_PUTWIN && HAVE_GETWIN
3766         case CTRL('W'): /* save and delete window */
3767             if (current == current->next) {
3768                 transient(current, "Will not save/delete ONLY window");
3769                 break;
3770             } else if ((fp = fopen(DUMPFILE, "w")) == (FILE *) 0) {
3771                 transient(current, "Can't open screen dump file");
3772             } else {
3773                 (void) putwin(current->wind, fp);
3774                 (void) fclose(fp);
3775
3776                 current = delete_framed(current, TRUE);
3777             }
3778             break;
3779
3780         case CTRL('R'): /* restore window */
3781             if ((fp = fopen(DUMPFILE, "r")) == (FILE *) 0) {
3782                 transient(current, "Can't open screen dump file");
3783             } else {
3784                 neww = (FRAME *) calloc(1, sizeof(FRAME));
3785
3786                 neww->next = current->next;
3787                 neww->last = current;
3788                 neww->last->next = neww;
3789                 neww->next->last = neww;
3790
3791                 neww->wind = getwin(fp);
3792                 (void) fclose(fp);
3793
3794                 wrefresh(neww->wind);
3795             }
3796             break;
3797 #endif
3798
3799 #if HAVE_WRESIZE
3800         case CTRL('X'): /* resize window */
3801             if (current) {
3802                 pair *tmp, ul, lr;
3803                 int i, mx, my;
3804
3805                 move(0, 0);
3806                 clrtoeol();
3807                 addstr("Use arrows to move cursor, anything else to mark new corner");
3808                 refresh();
3809
3810                 getbegyx(current->wind, ul.y, ul.x);
3811
3812                 tmp = selectcell(ul.y, ul.x, LINES - BOTLINES - 2, COLS - 2);
3813                 if (tmp == (pair *) 0) {
3814                     beep();
3815                     break;
3816                 }
3817
3818                 getmaxyx(current->wind, lr.y, lr.x);
3819                 lr.y += (ul.y - 1);
3820                 lr.x += (ul.x - 1);
3821                 outerbox(ul, lr, FALSE);
3822                 wnoutrefresh(stdscr);
3823
3824                 /* strictly cosmetic hack for the test */
3825                 getmaxyx(current->wind, my, mx);
3826                 if (my > tmp->y - ul.y) {
3827                     getyx(current->wind, lr.y, lr.x);
3828                     wmove(current->wind, tmp->y - ul.y + 1, 0);
3829                     wclrtobot(current->wind);
3830                     wmove(current->wind, lr.y, lr.x);
3831                 }
3832                 if (mx > tmp->x - ul.x)
3833                     for (i = 0; i < my; i++) {
3834                         wmove(current->wind, i, tmp->x - ul.x + 1);
3835                         wclrtoeol(current->wind);
3836                     }
3837                 wnoutrefresh(current->wind);
3838
3839                 memcpy(&lr, tmp, sizeof(pair));
3840                 (void) wresize(current->wind, lr.y - ul.y + 0, lr.x - ul.x + 0);
3841
3842                 getbegyx(current->wind, ul.y, ul.x);
3843                 getmaxyx(current->wind, lr.y, lr.x);
3844                 lr.y += (ul.y - 1);
3845                 lr.x += (ul.x - 1);
3846                 outerbox(ul, lr, TRUE);
3847                 wnoutrefresh(stdscr);
3848
3849                 wnoutrefresh(current->wind);
3850                 move(0, 0);
3851                 clrtoeol();
3852                 doupdate();
3853             }
3854             break;
3855 #endif /* HAVE_WRESIZE */
3856
3857         case KEY_F(10): /* undocumented --- use this to test area clears */
3858             selectcell(0, 0, LINES - 1, COLS - 1);
3859             clrtobot();
3860             refresh();
3861             break;
3862
3863         case KEY_UP:
3864             newwin_move(current, -1, 0);
3865             break;
3866         case KEY_DOWN:
3867             newwin_move(current, 1, 0);
3868             break;
3869         case KEY_LEFT:
3870             newwin_move(current, 0, -1);
3871             break;
3872         case KEY_RIGHT:
3873             newwin_move(current, 0, 1);
3874             break;
3875
3876         case KEY_BACKSPACE:
3877             /* FALLTHROUGH */
3878         case KEY_DC:
3879             {
3880                 int y, x;
3881                 getyx(current->wind, y, x);
3882                 if (--x < 0) {
3883                     if (--y < 0)
3884                         break;
3885                     x = getmaxx(current->wind) - 1;
3886                 }
3887                 mvwdelch(current->wind, y, x);
3888             }
3889             break;
3890
3891         case '\r':
3892             c = '\n';
3893             /* FALLTHROUGH */
3894
3895         default:
3896             if (current)
3897                 waddch(current->wind, (chtype) c);
3898             else
3899                 beep();
3900             break;
3901         }
3902         newwin_report(current);
3903         usescr = (current ? current->wind : stdscr);
3904         wrefresh(usescr);
3905     } while
3906         (!isQuit(c = wGetchar(usescr))
3907          && (c != ERR));
3908
3909   breakout:
3910     while (current != 0)
3911         current = delete_framed(current, FALSE);
3912
3913     scrollok(stdscr, TRUE);     /* reset to driver's default */
3914 #ifdef NCURSES_MOUSE_VERSION
3915     mousemask(0, (mmask_t *) 0);
3916 #endif
3917     noraw();
3918     erase();
3919     endwin();
3920 }
3921
3922 /****************************************************************************
3923  *
3924  * Panels tester
3925  *
3926  ****************************************************************************/
3927
3928 #if USE_LIBPANEL
3929 static int nap_msec = 1;
3930
3931 static NCURSES_CONST char *mod[] =
3932 {
3933     "test ",
3934     "TEST ",
3935     "(**) ",
3936     "*()* ",
3937     "<--> ",
3938     "LAST "
3939 };
3940
3941 /*+-------------------------------------------------------------------------
3942         wait_a_while(msec)
3943 --------------------------------------------------------------------------*/
3944 static void
3945 wait_a_while(int msec GCC_UNUSED)
3946 {
3947 #if HAVE_NAPMS
3948     if (nap_msec == 1)
3949         wGetchar(stdscr);
3950     else
3951         napms(nap_msec);
3952 #else
3953     if (nap_msec == 1)
3954         wGetchar(stdscr);
3955     else if (msec > 1000)
3956         sleep((unsigned) msec / 1000);
3957     else
3958         sleep(1);
3959 #endif
3960 }                               /* end of wait_a_while */
3961
3962 /*+-------------------------------------------------------------------------
3963         saywhat(text)
3964 --------------------------------------------------------------------------*/
3965 static void
3966 saywhat(NCURSES_CONST char *text)
3967 {
3968     wmove(stdscr, LINES - 1, 0);
3969     wclrtoeol(stdscr);
3970     if (text != 0 && *text != '\0') {
3971         waddstr(stdscr, text);
3972         waddstr(stdscr, "; ");
3973     }
3974     waddstr(stdscr, "press any key to continue");
3975 }                               /* end of saywhat */
3976
3977 /*+-------------------------------------------------------------------------
3978         mkpanel(rows,cols,tly,tlx) - alloc a win and panel and associate them
3979 --------------------------------------------------------------------------*/
3980 static PANEL *
3981 mkpanel(short color, int rows, int cols, int tly, int tlx)
3982 {
3983     WINDOW *win;
3984     PANEL *pan = 0;
3985
3986     if ((win = newwin(rows, cols, tly, tlx)) != 0) {
3987         if ((pan = new_panel(win)) == 0) {
3988             delwin(win);
3989         } else if (use_colors) {
3990             short fg = (color == COLOR_BLUE) ? COLOR_WHITE : COLOR_BLACK;
3991             short bg = color;
3992
3993             init_pair(color, fg, bg);
3994             wbkgdset(win, (chtype) (COLOR_PAIR(color) | ' '));
3995         } else {
3996             wbkgdset(win, A_BOLD | ' ');
3997         }
3998     }
3999     return pan;
4000 }                               /* end of mkpanel */
4001
4002 /*+-------------------------------------------------------------------------
4003         rmpanel(pan)
4004 --------------------------------------------------------------------------*/
4005 static void
4006 rmpanel(PANEL * pan)
4007 {
4008     WINDOW *win = panel_window(pan);
4009     del_panel(pan);
4010     delwin(win);
4011 }                               /* end of rmpanel */
4012
4013 /*+-------------------------------------------------------------------------
4014         pflush()
4015 --------------------------------------------------------------------------*/
4016 static void
4017 pflush(void)
4018 {
4019     update_panels();
4020     doupdate();
4021 }                               /* end of pflush */
4022
4023 /*+-------------------------------------------------------------------------
4024         fill_panel(win)
4025 --------------------------------------------------------------------------*/
4026 static void
4027 init_panel(void)
4028 {
4029     register int y, x;
4030
4031     for (y = 0; y < LINES - 1; y++) {
4032         for (x = 0; x < COLS; x++)
4033             wprintw(stdscr, "%d", (y + x) % 10);
4034     }
4035 }
4036
4037 static void
4038 fill_panel(PANEL * pan)
4039 {
4040     WINDOW *win = panel_window(pan);
4041     int num = ((const char *) panel_userptr(pan))[1];
4042     int y, x;
4043
4044     wmove(win, 1, 1);
4045     wprintw(win, "-pan%c-", num);
4046     wclrtoeol(win);
4047     box(win, 0, 0);
4048     for (y = 2; y < getmaxy(win) - 1; y++) {
4049         for (x = 1; x < getmaxx(win) - 1; x++) {
4050             wmove(win, y, x);
4051             waddch(win, UChar(num));
4052         }
4053     }
4054 }
4055
4056 #if USE_WIDEC_SUPPORT
4057 static void
4058 make_fullwidth_digit(cchar_t *target, int digit)
4059 {
4060     wchar_t source[2];
4061
4062     source[0] = digit + 0xff10;
4063     source[1] = 0;
4064     setcchar(target, source, A_NORMAL, 0, 0);
4065 }
4066
4067 static void
4068 init_wide_panel(void)
4069 {
4070     int digit;
4071     cchar_t temp[10];
4072
4073     for (digit = 0; digit < 10; ++digit)
4074         make_fullwidth_digit(&temp[digit], digit);
4075
4076     do {
4077         int y, x;
4078         getyx(stdscr, y, x);
4079         digit = (y + x / 2) % 10;
4080     } while (add_wch(&temp[digit]) != ERR);
4081 }
4082
4083 static void
4084 fill_wide_panel(PANEL * pan)
4085 {
4086     WINDOW *win = panel_window(pan);
4087     int num = ((const char *) panel_userptr(pan))[1];
4088     int y, x;
4089
4090     wmove(win, 1, 1);
4091     wprintw(win, "-pan%c-", num);
4092     wclrtoeol(win);
4093     box(win, 0, 0);
4094     for (y = 2; y < getmaxy(win) - 1; y++) {
4095         for (x = 1; x < getmaxx(win) - 1; x++) {
4096             wmove(win, y, x);
4097             waddch(win, UChar(num));
4098         }
4099     }
4100 }
4101 #endif
4102
4103 #define MAX_PANELS 5
4104
4105 static void
4106 canned_panel(PANEL * px[MAX_PANELS + 1], NCURSES_CONST char *cmd)
4107 {
4108     int which = cmd[1] - '0';
4109
4110     saywhat(cmd);
4111     switch (*cmd) {
4112     case 'h':
4113         hide_panel(px[which]);
4114         break;
4115     case 's':
4116         show_panel(px[which]);
4117         break;
4118     case 't':
4119         top_panel(px[which]);
4120         break;
4121     case 'b':
4122         bottom_panel(px[which]);
4123         break;
4124     case 'd':
4125         rmpanel(px[which]);
4126         break;
4127     }
4128     pflush();
4129     wait_a_while(nap_msec);
4130 }
4131
4132 static void
4133 demo_panels(void (*InitPanel) (void), void (*FillPanel) (PANEL *))
4134 {
4135     int count;
4136     int itmp;
4137     PANEL *px[MAX_PANELS + 1];
4138
4139     scrollok(stdscr, FALSE);    /* we don't want stdscr to scroll! */
4140     refresh();
4141
4142     InitPanel();
4143     for (count = 0; count < 5; count++) {
4144         px[1] = mkpanel(COLOR_RED,
4145                         LINES / 2 - 2,
4146                         COLS / 8 + 1,
4147                         0,
4148                         0);
4149         set_panel_userptr(px[1], (NCURSES_CONST void *) "p1");
4150
4151         px[2] = mkpanel(COLOR_GREEN,
4152                         LINES / 2 + 1,
4153                         COLS / 7,
4154                         LINES / 4,
4155                         COLS / 10);
4156         set_panel_userptr(px[2], (NCURSES_CONST void *) "p2");
4157
4158         px[3] = mkpanel(COLOR_YELLOW,
4159                         LINES / 4,
4160                         COLS / 10,
4161                         LINES / 2,
4162                         COLS / 9);
4163         set_panel_userptr(px[3], (NCURSES_CONST void *) "p3");
4164
4165         px[4] = mkpanel(COLOR_BLUE,
4166                         LINES / 2 - 2,
4167                         COLS / 8,
4168                         LINES / 2 - 2,
4169                         COLS / 3);
4170         set_panel_userptr(px[4], (NCURSES_CONST void *) "p4");
4171
4172         px[5] = mkpanel(COLOR_MAGENTA,
4173                         LINES / 2 - 2,
4174                         COLS / 8,
4175                         LINES / 2,
4176                         COLS / 2 - 2);
4177         set_panel_userptr(px[5], (NCURSES_CONST void *) "p5");
4178
4179         FillPanel(px[1]);
4180         FillPanel(px[2]);
4181         FillPanel(px[3]);
4182         FillPanel(px[4]);
4183         FillPanel(px[5]);
4184
4185         hide_panel(px[4]);
4186         hide_panel(px[5]);
4187         pflush();
4188         saywhat("");
4189         wait_a_while(nap_msec);
4190
4191         saywhat("h3 s1 s2 s4 s5");
4192         move_panel(px[1], 0, 0);
4193         hide_panel(px[3]);
4194         show_panel(px[1]);
4195         show_panel(px[2]);
4196         show_panel(px[4]);
4197         show_panel(px[5]);
4198         pflush();
4199         wait_a_while(nap_msec);
4200
4201         canned_panel(px, "s1");
4202         canned_panel(px, "s2");
4203
4204         saywhat("m2");
4205         move_panel(px[2], LINES / 3 + 1, COLS / 8);
4206         pflush();
4207         wait_a_while(nap_msec);
4208
4209         canned_panel(px, "s3");
4210
4211         saywhat("m3");
4212         move_panel(px[3], LINES / 4 + 1, COLS / 15);
4213         pflush();
4214         wait_a_while(nap_msec);
4215
4216         canned_panel(px, "b3");
4217         canned_panel(px, "s4");
4218         canned_panel(px, "s5");
4219         canned_panel(px, "t3");
4220         canned_panel(px, "t1");
4221         canned_panel(px, "t2");
4222         canned_panel(px, "t3");
4223         canned_panel(px, "t4");
4224
4225         for (itmp = 0; itmp < 6; itmp++) {
4226             WINDOW *w4 = panel_window(px[4]);
4227             WINDOW *w5 = panel_window(px[5]);
4228
4229             saywhat("m4");
4230             wmove(w4, LINES / 8, 1);
4231             waddstr(w4, mod[itmp]);
4232             move_panel(px[4], LINES / 6, itmp * (COLS / 8));
4233             wmove(w5, LINES / 6, 1);
4234             waddstr(w5, mod[itmp]);
4235             pflush();
4236             wait_a_while(nap_msec);
4237
4238             saywhat("m5");
4239             wmove(w4, LINES / 6, 1);
4240             waddstr(w4, mod[itmp]);
4241             move_panel(px[5], LINES / 3 - 1, (itmp * 10) + 6);
4242             wmove(w5, LINES / 8, 1);
4243             waddstr(w5, mod[itmp]);
4244             pflush();
4245             wait_a_while(nap_msec);
4246         }
4247
4248         saywhat("m4");
4249         move_panel(px[4], LINES / 6, itmp * (COLS / 8));
4250         pflush();
4251         wait_a_while(nap_msec);
4252
4253         canned_panel(px, "t5");
4254         canned_panel(px, "t2");
4255         canned_panel(px, "t1");
4256         canned_panel(px, "d2");
4257         canned_panel(px, "h3");
4258         canned_panel(px, "d1");
4259         canned_panel(px, "d4");
4260         canned_panel(px, "d5");
4261         canned_panel(px, "d3");
4262
4263         wait_a_while(nap_msec);
4264         if (nap_msec == 1)
4265             break;
4266         nap_msec = 100L;
4267     }
4268
4269     erase();
4270     endwin();
4271 }
4272 #endif /* USE_LIBPANEL */
4273
4274 /****************************************************************************
4275  *
4276  * Pad tester
4277  *
4278  ****************************************************************************/
4279
4280 #define GRIDSIZE        3
4281
4282 static bool pending_pan = FALSE;
4283 static bool show_panner_legend = TRUE;
4284
4285 static int
4286 panner_legend(int line)
4287 {
4288     static const char *const legend[] =
4289     {
4290         "Use arrow keys (or U,D,L,R) to pan, ESC to quit, ! to shell-out.",
4291         "Use +,- (or j,k) to grow/shrink the panner vertically.",
4292         "Use <,> (or h,l) to grow/shrink the panner horizontally.",
4293         "Number repeats.  Toggle legend:? filler:a timer:t scrollmark:s."
4294     };
4295     int n = (SIZEOF(legend) - (LINES - line));
4296     if (line < LINES && (n >= 0)) {
4297         move(line, 0);
4298         if (show_panner_legend)
4299             printw("%s", legend[n]);
4300         clrtoeol();
4301         return show_panner_legend;
4302     }
4303     return FALSE;
4304 }
4305
4306 static void
4307 panner_h_cleanup(int from_y, int from_x, int to_x)
4308 {
4309     if (!panner_legend(from_y))
4310         do_h_line(from_y, from_x, ' ', to_x);
4311 }
4312
4313 static void
4314 panner_v_cleanup(int from_y, int from_x, int to_y)
4315 {
4316     if (!panner_legend(from_y))
4317         do_v_line(from_y, from_x, ' ', to_y);
4318 }
4319
4320 static void
4321 fill_pad(WINDOW *panpad, bool pan_lines)
4322 {
4323     int y, x;
4324     unsigned gridcount = 0;
4325
4326     wmove(panpad, 0, 0);
4327     for (y = 0; y < getmaxy(panpad); y++) {
4328         for (x = 0; x < getmaxx(panpad); x++) {
4329             if (y % GRIDSIZE == 0 && x % GRIDSIZE == 0) {
4330                 if (y == 0 && x == 0)
4331                     waddch(panpad, pan_lines ? ACS_ULCORNER : '+');
4332                 else if (y == 0)
4333                     waddch(panpad, pan_lines ? ACS_TTEE : '+');
4334                 else if (y == 0 || x == 0)
4335                     waddch(panpad, pan_lines ? ACS_LTEE : '+');
4336                 else
4337                     waddch(panpad, (chtype) ((pan_lines ? 'a' : 'A') +
4338                                              (gridcount++ % 26)));
4339             } else if (y % GRIDSIZE == 0)
4340                 waddch(panpad, pan_lines ? ACS_HLINE : '-');
4341             else if (x % GRIDSIZE == 0)
4342                 waddch(panpad, pan_lines ? ACS_VLINE : '|');
4343             else
4344                 waddch(panpad, ' ');
4345         }
4346     }
4347 }
4348
4349 static void
4350 panner(WINDOW *pad,
4351        int top_x, int top_y, int porty, int portx,
4352        int (*pgetc) (WINDOW *))
4353 {
4354 #if HAVE_GETTIMEOFDAY
4355     struct timeval before, after;
4356     bool timing = TRUE;
4357 #endif
4358     bool pan_lines = FALSE;
4359     bool scrollers = TRUE;
4360     int basex = 0;
4361     int basey = 0;
4362     int pxmax, pymax, lowend, highend, c;
4363
4364     getmaxyx(pad, pymax, pxmax);
4365     scrollok(stdscr, FALSE);    /* we don't want stdscr to scroll! */
4366
4367     c = KEY_REFRESH;
4368     do {
4369 #ifdef NCURSES_VERSION
4370         /*
4371          * During shell-out, the user may have resized the window.  Adjust
4372          * the port size of the pad to accommodate this.  Ncurses automatically
4373          * resizes all of the normal windows to fit on the new screen.
4374          */
4375         if (top_x > COLS)
4376             top_x = COLS;
4377         if (portx > COLS)
4378             portx = COLS;
4379         if (top_y > LINES)
4380             top_y = LINES;
4381         if (porty > LINES)
4382             porty = LINES;
4383 #endif
4384         switch (c) {
4385         case KEY_REFRESH:
4386             erase();
4387
4388             /* FALLTHRU */
4389         case '?':
4390             if (c == '?')
4391                 show_panner_legend = !show_panner_legend;
4392             panner_legend(LINES - 4);
4393             panner_legend(LINES - 3);
4394             panner_legend(LINES - 2);
4395             panner_legend(LINES - 1);
4396             break;
4397         case 'a':
4398             pan_lines = !pan_lines;
4399             fill_pad(pad, pan_lines);
4400             pending_pan = FALSE;
4401             break;
4402
4403 #if HAVE_GETTIMEOFDAY
4404         case 't':
4405             timing = !timing;
4406             if (!timing)
4407                 panner_legend(LINES - 1);
4408             break;
4409 #endif
4410         case 's':
4411             scrollers = !scrollers;
4412             break;
4413
4414             /* Move the top-left corner of the pad, keeping the bottom-right
4415              * corner fixed.
4416              */
4417         case 'h':               /* increase-columns: move left edge to left */
4418             if (top_x <= 0)
4419                 beep();
4420             else {
4421                 panner_v_cleanup(top_y, top_x, porty);
4422                 top_x--;
4423             }
4424             break;
4425
4426         case 'j':               /* decrease-lines: move top-edge down */
4427             if (top_y >= porty)
4428                 beep();
4429             else {
4430                 panner_h_cleanup(top_y - 1, top_x - (top_x > 0), portx);
4431                 top_y++;
4432             }
4433             break;
4434
4435         case 'k':               /* increase-lines: move top-edge up */
4436             if (top_y <= 0)
4437                 beep();
4438             else {
4439                 top_y--;
4440                 panner_h_cleanup(top_y, top_x, portx);
4441             }
4442             break;
4443
4444         case 'l':               /* decrease-columns: move left-edge to right */
4445             if (top_x >= portx)
4446                 beep();
4447             else {
4448                 panner_v_cleanup(top_y - (top_y > 0), top_x - 1, porty);
4449                 top_x++;
4450             }
4451             break;
4452
4453             /* Move the bottom-right corner of the pad, keeping the top-left
4454              * corner fixed.
4455              */
4456         case KEY_IC:            /* increase-columns: move right-edge to right */
4457             if (portx >= pxmax || portx >= COLS)
4458                 beep();
4459             else {
4460                 panner_v_cleanup(top_y - (top_y > 0), portx - 1, porty);
4461                 ++portx;
4462             }
4463             break;
4464
4465         case KEY_IL:            /* increase-lines: move bottom-edge down */
4466             if (porty >= pymax || porty >= LINES)
4467                 beep();
4468             else {
4469                 panner_h_cleanup(porty - 1, top_x - (top_x > 0), portx);
4470                 ++porty;
4471             }
4472             break;
4473
4474         case KEY_DC:            /* decrease-columns: move bottom edge up */
4475             if (portx <= top_x)
4476                 beep();
4477             else {
4478                 portx--;
4479                 panner_v_cleanup(top_y - (top_y > 0), portx, porty);
4480             }
4481             break;
4482
4483         case KEY_DL:            /* decrease-lines */
4484             if (porty <= top_y)
4485                 beep();
4486             else {
4487                 porty--;
4488                 panner_h_cleanup(porty, top_x - (top_x > 0), portx);
4489             }
4490             break;
4491
4492         case KEY_LEFT:          /* pan leftwards */
4493             if (basex > 0)
4494                 basex--;
4495             else
4496                 beep();
4497             break;
4498
4499         case KEY_RIGHT: /* pan rightwards */
4500             if (basex + portx - (pymax > porty) < pxmax)
4501                 basex++;
4502             else
4503                 beep();
4504             break;
4505
4506         case KEY_UP:            /* pan upwards */
4507             if (basey > 0)
4508                 basey--;
4509             else
4510                 beep();
4511             break;
4512
4513         case KEY_DOWN:          /* pan downwards */
4514             if (basey + porty - (pxmax > portx) < pymax)
4515                 basey++;
4516             else
4517                 beep();
4518             break;
4519
4520         case 'H':
4521         case KEY_HOME:
4522         case KEY_FIND:
4523             basey = 0;
4524             break;
4525
4526         case 'E':
4527         case KEY_END:
4528         case KEY_SELECT:
4529             basey = pymax - porty;
4530             if (basey < 0)
4531                 basey = 0;
4532             break;
4533
4534         default:
4535             beep();
4536             break;
4537         }
4538
4539         mvaddch(top_y - 1, top_x - 1, ACS_ULCORNER);
4540         do_v_line(top_y, top_x - 1, ACS_VLINE, porty);
4541         do_h_line(top_y - 1, top_x, ACS_HLINE, portx);
4542
4543         if (scrollers && (pxmax > portx - 1)) {
4544             int length = (portx - top_x - 1);
4545             float ratio = ((float) length) / ((float) pxmax);
4546
4547             lowend = (int) (top_x + (basex * ratio));
4548             highend = (int) (top_x + ((basex + length) * ratio));
4549
4550             do_h_line(porty - 1, top_x, ACS_HLINE, lowend);
4551             if (highend < portx) {
4552                 attron(A_REVERSE);
4553                 do_h_line(porty - 1, lowend, ' ', highend + 1);
4554                 attroff(A_REVERSE);
4555                 do_h_line(porty - 1, highend + 1, ACS_HLINE, portx);
4556             }
4557         } else
4558             do_h_line(porty - 1, top_x, ACS_HLINE, portx);
4559
4560         if (scrollers && (pymax > porty - 1)) {
4561             int length = (porty - top_y - 1);
4562             float ratio = ((float) length) / ((float) pymax);
4563
4564             lowend = (int) (top_y + (basey * ratio));
4565             highend = (int) (top_y + ((basey + length) * ratio));
4566
4567             do_v_line(top_y, portx - 1, ACS_VLINE, lowend);
4568             if (highend < porty) {
4569                 attron(A_REVERSE);
4570                 do_v_line(lowend, portx - 1, ' ', highend + 1);
4571                 attroff(A_REVERSE);
4572                 do_v_line(highend + 1, portx - 1, ACS_VLINE, porty);
4573             }
4574         } else
4575             do_v_line(top_y, portx - 1, ACS_VLINE, porty);
4576
4577         mvaddch(top_y - 1, portx - 1, ACS_URCORNER);
4578         mvaddch(porty - 1, top_x - 1, ACS_LLCORNER);
4579         mvaddch(porty - 1, portx - 1, ACS_LRCORNER);
4580
4581         if (!pending_pan) {
4582 #if HAVE_GETTIMEOFDAY
4583             gettimeofday(&before, 0);
4584 #endif
4585             wnoutrefresh(stdscr);
4586
4587             pnoutrefresh(pad,
4588                          basey, basex,
4589                          top_y, top_x,
4590                          porty - (pxmax > portx) - 1,
4591                          portx - (pymax > porty) - 1);
4592
4593             doupdate();
4594 #if HAVE_GETTIMEOFDAY
4595             if (timing) {
4596                 double elapsed;
4597                 gettimeofday(&after, 0);
4598                 elapsed = (after.tv_sec + after.tv_usec / 1.0e6)
4599                     - (before.tv_sec + before.tv_usec / 1.0e6);
4600                 move(LINES - 1, COLS - 12);
4601                 printw("Secs: %2.03f", elapsed);
4602                 refresh();
4603             }
4604 #endif
4605         }
4606
4607     } while
4608         ((c = pgetc(pad)) != KEY_EXIT);
4609
4610     scrollok(stdscr, TRUE);     /* reset to driver's default */
4611 }
4612
4613 static int
4614 padgetch(WINDOW *win)
4615 {
4616     static int count;
4617     static int last;
4618     int c;
4619
4620     if ((pending_pan = (count > 0)) != FALSE) {
4621         count--;
4622         pending_pan = (count != 0);
4623     } else {
4624         for (;;) {
4625             switch (c = wGetchar(win)) {
4626             case '!':
4627                 ShellOut(FALSE);
4628                 /* FALLTHRU */
4629             case CTRL('r'):
4630                 endwin();
4631                 refresh();
4632                 c = KEY_REFRESH;
4633                 break;
4634             case CTRL('l'):
4635                 c = KEY_REFRESH;
4636                 break;
4637             case 'U':
4638                 c = KEY_UP;
4639                 break;
4640             case 'D':
4641                 c = KEY_DOWN;
4642                 break;
4643             case 'R':
4644                 c = KEY_RIGHT;
4645                 break;
4646             case 'L':
4647                 c = KEY_LEFT;
4648                 break;
4649             case '+':
4650                 c = KEY_IL;
4651                 break;
4652             case '-':
4653                 c = KEY_DL;
4654                 break;
4655             case '>':
4656                 c = KEY_IC;
4657                 break;
4658             case '<':
4659                 c = KEY_DC;
4660                 break;
4661             case ERR:           /* FALLTHRU */
4662               case_QUIT:
4663                 count = 0;
4664                 c = KEY_EXIT;
4665                 break;
4666             default:
4667                 if (c >= '0' && c <= '9') {
4668                     count = count * 10 + (c - '0');
4669                     continue;
4670                 }
4671                 break;
4672             }
4673             last = c;
4674             break;
4675         }
4676         if (count > 0)
4677             count--;
4678     }
4679     return (last);
4680 }
4681
4682 #define PAD_HIGH 200
4683 #define PAD_WIDE 200
4684
4685 static void
4686 demo_pad(void)
4687 /* Demonstrate pads. */
4688 {
4689     WINDOW *panpad = newpad(PAD_HIGH, PAD_WIDE);
4690
4691     if (panpad == 0) {
4692         Cannot("cannot create requested pad");
4693         return;
4694     }
4695
4696     fill_pad(panpad, FALSE);
4697
4698     panner_legend(LINES - 4);
4699     panner_legend(LINES - 3);
4700     panner_legend(LINES - 2);
4701     panner_legend(LINES - 1);
4702
4703     keypad(panpad, TRUE);
4704
4705     /* Make the pad (initially) narrow enough that a trace file won't wrap.
4706      * We'll still be able to widen it during a test, since that's required
4707      * for testing boundaries.
4708      */
4709     panner(panpad, 2, 2, LINES - 5, COLS - 15, padgetch);
4710
4711     delwin(panpad);
4712     endwin();
4713     erase();
4714 }
4715
4716 /****************************************************************************
4717  *
4718  * Tests from John Burnell's PDCurses tester
4719  *
4720  ****************************************************************************/
4721
4722 static void
4723 Continue(WINDOW *win)
4724 {
4725     noecho();
4726     wmove(win, 10, 1);
4727     mvwaddstr(win, 10, 1, " Press any key to continue");
4728     wrefresh(win);
4729     wGetchar(win);
4730 }
4731
4732 static void
4733 flushinp_test(WINDOW *win)
4734 /* Input test, adapted from John Burnell's PDCurses tester */
4735 {
4736     int w, h, bx, by, sw, sh, i;
4737
4738     WINDOW *subWin;
4739     wclear(win);
4740
4741     getmaxyx(win, h, w);
4742     getbegyx(win, by, bx);
4743     sw = w / 3;
4744     sh = h / 3;
4745     if ((subWin = subwin(win, sh, sw, by + h - sh - 2, bx + w - sw - 2)) == 0)
4746         return;
4747
4748 #ifdef A_COLOR
4749     if (use_colors) {
4750         init_pair(2, COLOR_CYAN, COLOR_BLUE);
4751         wbkgd(subWin, COLOR_PAIR(2) | ' ');
4752     }
4753 #endif
4754     wattrset(subWin, A_BOLD);
4755     box(subWin, ACS_VLINE, ACS_HLINE);
4756     mvwaddstr(subWin, 2, 1, "This is a subwindow");
4757     wrefresh(win);
4758
4759     /*
4760      * This used to set 'nocbreak()'.  However, Alexander Lukyanov says that
4761      * it only happened to "work" on SVr4 because that implementation does not
4762      * emulate nocbreak+noecho mode, whereas ncurses does.  To get the desired
4763      * test behavior, we're using 'cbreak()', which will allow a single
4764      * character to return without needing a newline. - T.Dickey 1997/10/11.
4765      */
4766     cbreak();
4767     mvwaddstr(win, 0, 1, "This is a test of the flushinp() call.");
4768
4769     mvwaddstr(win, 2, 1, "Type random keys for 5 seconds.");
4770     mvwaddstr(win, 3, 1,
4771               "These should be discarded (not echoed) after the subwindow goes away.");
4772     wrefresh(win);
4773
4774     for (i = 0; i < 5; i++) {
4775         mvwprintw(subWin, 1, 1, "Time = %d", i);
4776         wrefresh(subWin);
4777         napms(1000);
4778         flushinp();
4779     }
4780
4781     delwin(subWin);
4782     werase(win);
4783     flash();
4784     wrefresh(win);
4785     napms(1000);
4786
4787     mvwaddstr(win, 2, 1,
4788               "If you were still typing when the window timer expired,");
4789     mvwaddstr(win, 3, 1,
4790               "or else you typed nothing at all while it was running,");
4791     mvwaddstr(win, 4, 1,
4792               "test was invalid.  You'll see garbage or nothing at all. ");
4793     mvwaddstr(win, 6, 1, "Press a key");
4794     wmove(win, 9, 10);
4795     wrefresh(win);
4796     echo();
4797     wGetchar(win);
4798     flushinp();
4799     mvwaddstr(win, 12, 0,
4800               "If you see any key other than what you typed, flushinp() is broken.");
4801     Continue(win);
4802
4803     wmove(win, 9, 10);
4804     wdelch(win);
4805     wrefresh(win);
4806     wmove(win, 12, 0);
4807     clrtoeol();
4808     waddstr(win,
4809             "What you typed should now have been deleted; if not, wdelch() failed.");
4810     Continue(win);
4811
4812     cbreak();
4813 }
4814
4815 /****************************************************************************
4816  *
4817  * Menu test
4818  *
4819  ****************************************************************************/
4820
4821 #if USE_LIBMENU
4822
4823 #define MENU_Y  8
4824 #define MENU_X  8
4825
4826 static int
4827 menu_virtualize(int c)
4828 {
4829     if (c == '\n' || c == KEY_EXIT)
4830         return (MAX_COMMAND + 1);
4831     else if (c == 'u')
4832         return (REQ_SCR_ULINE);
4833     else if (c == 'd')
4834         return (REQ_SCR_DLINE);
4835     else if (c == 'b' || c == KEY_NPAGE)
4836         return (REQ_SCR_UPAGE);
4837     else if (c == 'f' || c == KEY_PPAGE)
4838         return (REQ_SCR_DPAGE);
4839     else if (c == 'n' || c == KEY_DOWN)
4840         return (REQ_NEXT_ITEM);
4841     else if (c == 'p' || c == KEY_UP)
4842         return (REQ_PREV_ITEM);
4843     else if (c == ' ')
4844         return (REQ_TOGGLE_ITEM);
4845     else {
4846         if (c != KEY_MOUSE)
4847             beep();
4848         return (c);
4849     }
4850 }
4851
4852 static CONST_MENUS char *animals[] =
4853 {
4854     "Lions",
4855     "Tigers",
4856     "Bears",
4857     "(Oh my!)",
4858     "Newts",
4859     "Platypi",
4860     "Lemurs",
4861     "(Oh really?!)",
4862     "Leopards",
4863     "Panthers",
4864     "Pumas",
4865     "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
4866     "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs, Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
4867     (char *) 0
4868 };
4869
4870 static void
4871 menu_test(void)
4872 {
4873     MENU *m;
4874     ITEM *items[SIZEOF(animals)];
4875     ITEM **ip = items;
4876     CONST_MENUS char **ap;
4877     int mrows, mcols, c;
4878     WINDOW *menuwin;
4879
4880 #ifdef NCURSES_MOUSE_VERSION
4881     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
4882 #endif
4883     mvaddstr(0, 0, "This is the menu test:");
4884     mvaddstr(2, 0, "  Use up and down arrow to move the select bar.");
4885     mvaddstr(3, 0, "  'n' and 'p' act like arrows.");
4886     mvaddstr(4, 0,
4887              "  'b' and 'f' scroll up/down (page), 'u' and 'd' (line).");
4888     mvaddstr(5, 0, "  Press return to exit.");
4889     refresh();
4890
4891     for (ap = animals; *ap; ap++)
4892         *ip++ = new_item(*ap, "");
4893     *ip = (ITEM *) 0;
4894
4895     m = new_menu(items);
4896
4897     set_menu_format(m, (SIZEOF(animals) + 1) / 2, 1);
4898     scale_menu(m, &mrows, &mcols);
4899
4900     menuwin = newwin(mrows + 2, mcols + 2, MENU_Y, MENU_X);
4901     set_menu_win(m, menuwin);
4902     keypad(menuwin, TRUE);
4903     box(menuwin, 0, 0);
4904
4905     set_menu_sub(m, derwin(menuwin, mrows, mcols, 1, 1));
4906
4907     post_menu(m);
4908
4909     while ((c = menu_driver(m, menu_virtualize(wGetchar(menuwin)))) != E_UNKNOWN_COMMAND) {
4910         if (c == E_NOT_POSTED)
4911             break;
4912         if (c == E_REQUEST_DENIED)
4913             beep();
4914         continue;
4915     }
4916
4917     (void) mvprintw(LINES - 2, 0,
4918                     "You chose: %s\n", item_name(current_item(m)));
4919     (void) addstr("Press any key to continue...");
4920     wGetchar(stdscr);
4921
4922     unpost_menu(m);
4923     delwin(menuwin);
4924
4925     free_menu(m);
4926     for (ip = items; *ip; ip++)
4927         free_item(*ip);
4928 #ifdef NCURSES_MOUSE_VERSION
4929     mousemask(0, (mmask_t *) 0);
4930 #endif
4931 }
4932
4933 #ifdef TRACE
4934 #define T_TBL(name) { #name, name }
4935 static struct {
4936     const char *name;
4937     unsigned mask;
4938 } t_tbl[] = {
4939
4940     T_TBL(TRACE_DISABLE),
4941         T_TBL(TRACE_TIMES),
4942         T_TBL(TRACE_TPUTS),
4943         T_TBL(TRACE_UPDATE),
4944         T_TBL(TRACE_MOVE),
4945         T_TBL(TRACE_CHARPUT),
4946         T_TBL(TRACE_ORDINARY),
4947         T_TBL(TRACE_CALLS),
4948         T_TBL(TRACE_VIRTPUT),
4949         T_TBL(TRACE_IEVENT),
4950         T_TBL(TRACE_BITS),
4951         T_TBL(TRACE_ICALLS),
4952         T_TBL(TRACE_CCALLS),
4953         T_TBL(TRACE_DATABASE),
4954         T_TBL(TRACE_ATTRS),
4955         T_TBL(TRACE_MAXIMUM),
4956     {
4957         (char *) 0, 0
4958     }
4959 };
4960
4961 static char *
4962 tracetrace(unsigned tlevel)
4963 {
4964     static char *buf;
4965     int n;
4966
4967     if (buf == 0) {
4968         size_t need = 12;
4969         for (n = 0; t_tbl[n].name != 0; n++)
4970             need += strlen(t_tbl[n].name) + 2;
4971         buf = (char *) malloc(need);
4972     }
4973     sprintf(buf, "0x%02x = {", tlevel);
4974     if (tlevel == 0) {
4975         sprintf(buf + strlen(buf), "%s, ", t_tbl[0].name);
4976     } else {
4977         for (n = 1; t_tbl[n].name != 0; n++)
4978             if ((tlevel & t_tbl[n].mask) == t_tbl[n].mask) {
4979                 strcat(buf, t_tbl[n].name);
4980                 strcat(buf, ", ");
4981             }
4982     }
4983     if (buf[strlen(buf) - 2] == ',')
4984         buf[strlen(buf) - 2] = '\0';
4985     return (strcat(buf, "}"));
4986 }
4987
4988 /* fake a dynamically reconfigurable menu using the 0th entry to deselect
4989  * the others
4990  */
4991 static int
4992 run_trace_menu(MENU * m)
4993 {
4994     ITEM **items;
4995     ITEM *i, **p;
4996
4997     for (;;) {
4998         bool changed = FALSE;
4999         switch (menu_driver(m, menu_virtualize(wGetchar(menu_win(m))))) {
5000         case E_UNKNOWN_COMMAND:
5001             return FALSE;
5002         default:
5003             items = menu_items(m);
5004             i = current_item(m);
5005             if (i == items[0]) {
5006                 if (item_value(i)) {
5007                     for (p = items + 1; *p != 0; p++)
5008                         if (item_value(*p)) {
5009                             set_item_value(*p, FALSE);
5010                             changed = TRUE;
5011                         }
5012                 }
5013             } else {
5014                 for (p = items + 1; *p != 0; p++)
5015                     if (item_value(*p)) {
5016                         set_item_value(items[0], FALSE);
5017                         changed = TRUE;
5018                         break;
5019                     }
5020             }
5021             if (!changed)
5022                 return TRUE;
5023         }
5024     }
5025 }
5026
5027 static void
5028 trace_set(void)
5029 /* interactively set the trace level */
5030 {
5031     MENU *m;
5032     ITEM *items[SIZEOF(t_tbl)];
5033     ITEM **ip = items;
5034     int mrows, mcols;
5035     unsigned newtrace;
5036     int n;
5037     WINDOW *menuwin;
5038
5039     mvaddstr(0, 0, "Interactively set trace level:");
5040     mvaddstr(2, 0, "  Press space bar to toggle a selection.");
5041     mvaddstr(3, 0, "  Use up and down arrow to move the select bar.");
5042     mvaddstr(4, 0, "  Press return to set the trace level.");
5043     mvprintw(6, 0, "(Current trace level is %s)", tracetrace(_nc_tracing));
5044
5045     refresh();
5046
5047     for (n = 0; t_tbl[n].name != 0; n++)
5048         *ip++ = new_item(t_tbl[n].name, "");
5049     *ip = (ITEM *) 0;
5050
5051     m = new_menu(items);
5052
5053     set_menu_format(m, 0, 2);
5054     scale_menu(m, &mrows, &mcols);
5055
5056     menu_opts_off(m, O_ONEVALUE);
5057     menuwin = newwin(mrows + 2, mcols + 2, MENU_Y, MENU_X);
5058     set_menu_win(m, menuwin);
5059     keypad(menuwin, TRUE);
5060     box(menuwin, 0, 0);
5061
5062     set_menu_sub(m, derwin(menuwin, mrows, mcols, 1, 1));
5063
5064     post_menu(m);
5065
5066     for (ip = menu_items(m); *ip; ip++) {
5067         unsigned mask = t_tbl[item_index(*ip)].mask;
5068         if (mask == 0)
5069             set_item_value(*ip, _nc_tracing == 0);
5070         else if ((mask & _nc_tracing) == mask)
5071             set_item_value(*ip, TRUE);
5072     }
5073
5074     while (run_trace_menu(m))
5075         continue;
5076
5077     newtrace = 0;
5078     for (ip = menu_items(m); *ip; ip++)
5079         if (item_value(*ip))
5080             newtrace |= t_tbl[item_index(*ip)].mask;
5081     trace(newtrace);
5082     Trace(("trace level interactively set to %s", tracetrace(_nc_tracing)));
5083
5084     (void) mvprintw(LINES - 2, 0,
5085                     "Trace level is %s\n", tracetrace(_nc_tracing));
5086     (void) addstr("Press any key to continue...");
5087     wGetchar(stdscr);
5088
5089     unpost_menu(m);
5090     delwin(menuwin);
5091
5092     free_menu(m);
5093     for (ip = items; *ip; ip++)
5094         free_item(*ip);
5095 }
5096 #endif /* TRACE */
5097 #endif /* USE_LIBMENU */
5098
5099 /****************************************************************************
5100  *
5101  * Forms test
5102  *
5103  ****************************************************************************/
5104 #if USE_LIBFORM
5105 static FIELD *
5106 make_label(int frow, int fcol, NCURSES_CONST char *label)
5107 {
5108     FIELD *f = new_field(1, (int) strlen(label), frow, fcol, 0, 0);
5109
5110     if (f) {
5111         set_field_buffer(f, 0, label);
5112         set_field_opts(f, (int) (field_opts(f) & ~O_ACTIVE));
5113     }
5114     return (f);
5115 }
5116
5117 static FIELD *
5118 make_field(int frow, int fcol, int rows, int cols, bool secure)
5119 {
5120     FIELD *f = new_field(rows, cols, frow, fcol, 0, secure ? 1 : 0);
5121
5122     if (f) {
5123         set_field_back(f, A_UNDERLINE);
5124         set_field_userptr(f, (void *) 0);
5125     }
5126     return (f);
5127 }
5128
5129 static void
5130 display_form(FORM * f)
5131 {
5132     WINDOW *w;
5133     int rows, cols;
5134
5135     scale_form(f, &rows, &cols);
5136
5137     if ((w = newwin(rows + 2, cols + 4, 0, 0)) != (WINDOW *) 0) {
5138         set_form_win(f, w);
5139         set_form_sub(f, derwin(w, rows, cols, 1, 2));
5140         box(w, 0, 0);
5141         keypad(w, TRUE);
5142     }
5143
5144     if (post_form(f) != E_OK)
5145         wrefresh(w);
5146 }
5147
5148 static void
5149 erase_form(FORM * f)
5150 {
5151     WINDOW *w = form_win(f);
5152     WINDOW *s = form_sub(f);
5153
5154     unpost_form(f);
5155     werase(w);
5156     wrefresh(w);
5157     delwin(s);
5158     delwin(w);
5159 }
5160
5161 static int
5162 edit_secure(FIELD * me, int c)
5163 {
5164     int rows, cols, frow, fcol, nrow, nbuf;
5165
5166     if (field_info(me, &rows, &cols, &frow, &fcol, &nrow, &nbuf) == E_OK
5167         && nbuf > 0) {
5168         char *source = field_buffer(me, 1);
5169         char temp[80];
5170         long len;
5171
5172         strcpy(temp, source ? source : "");
5173         len = (long) (char *) field_userptr(me);
5174         if (c <= KEY_MAX) {
5175             if (isgraph(c) && (len + 1) < (int) sizeof(temp)) {
5176                 temp[len++] = c;
5177                 temp[len] = 0;
5178                 set_field_buffer(me, 1, temp);
5179                 c = '*';
5180             } else {
5181                 c = 0;
5182             }
5183         } else {
5184             switch (c) {
5185             case REQ_BEG_FIELD:
5186             case REQ_CLR_EOF:
5187             case REQ_CLR_EOL:
5188             case REQ_DEL_LINE:
5189             case REQ_DEL_WORD:
5190             case REQ_DOWN_CHAR:
5191             case REQ_END_FIELD:
5192             case REQ_INS_CHAR:
5193             case REQ_INS_LINE:
5194             case REQ_LEFT_CHAR:
5195             case REQ_NEW_LINE:
5196             case REQ_NEXT_WORD:
5197             case REQ_PREV_WORD:
5198             case REQ_RIGHT_CHAR:
5199             case REQ_UP_CHAR:
5200                 c = 0;          /* we don't want to do inline editing */
5201                 break;
5202             case REQ_CLR_FIELD:
5203                 if (len) {
5204                     temp[0] = 0;
5205                     set_field_buffer(me, 1, temp);
5206                 }
5207                 break;
5208             case REQ_DEL_CHAR:
5209             case REQ_DEL_PREV:
5210                 if (len) {
5211                     temp[--len] = 0;
5212                     set_field_buffer(me, 1, temp);
5213                 }
5214                 break;
5215             }
5216         }
5217         set_field_userptr(me, (void *) len);
5218     }
5219     return c;
5220 }
5221
5222 static int
5223 form_virtualize(FORM * f, WINDOW *w)
5224 {
5225     /* *INDENT-OFF* */
5226     static const struct {
5227         int code;
5228         int result;
5229     } lookup[] = {
5230         { CTRL('A'),    REQ_NEXT_CHOICE },
5231         { CTRL('B'),    REQ_PREV_WORD },
5232         { CTRL('C'),    REQ_CLR_EOL },
5233         { CTRL('D'),    REQ_DOWN_FIELD },
5234         { CTRL('E'),    REQ_END_FIELD },
5235         { CTRL('F'),    REQ_NEXT_PAGE },
5236         { CTRL('G'),    REQ_DEL_WORD },
5237         { CTRL('H'),    REQ_DEL_PREV },
5238         { CTRL('I'),    REQ_INS_CHAR },
5239         { CTRL('K'),    REQ_CLR_EOF },
5240         { CTRL('L'),    REQ_LEFT_FIELD },
5241         { CTRL('M'),    REQ_NEW_LINE },
5242         { CTRL('N'),    REQ_NEXT_FIELD },
5243         { CTRL('O'),    REQ_INS_LINE },
5244         { CTRL('P'),    REQ_PREV_FIELD },
5245         { CTRL('R'),    REQ_RIGHT_FIELD },
5246         { CTRL('S'),    REQ_BEG_FIELD },
5247         { CTRL('U'),    REQ_UP_FIELD },
5248         { CTRL('V'),    REQ_DEL_CHAR },
5249         { CTRL('W'),    REQ_NEXT_WORD },
5250         { CTRL('X'),    REQ_CLR_FIELD },
5251         { CTRL('Y'),    REQ_DEL_LINE },
5252         { CTRL('Z'),    REQ_PREV_CHOICE },
5253         { ESCAPE,       MAX_FORM_COMMAND + 1 },
5254         { KEY_BACKSPACE, REQ_DEL_PREV },
5255         { KEY_DOWN,     REQ_DOWN_CHAR },
5256         { KEY_END,      REQ_LAST_FIELD },
5257         { KEY_HOME,     REQ_FIRST_FIELD },
5258         { KEY_LEFT,     REQ_LEFT_CHAR },
5259         { KEY_LL,       REQ_LAST_FIELD },
5260         { KEY_NEXT,     REQ_NEXT_FIELD },
5261         { KEY_NPAGE,    REQ_NEXT_PAGE },
5262         { KEY_PPAGE,    REQ_PREV_PAGE },
5263         { KEY_PREVIOUS, REQ_PREV_FIELD },
5264         { KEY_RIGHT,    REQ_RIGHT_CHAR },
5265         { KEY_UP,       REQ_UP_CHAR },
5266         { QUIT,         MAX_FORM_COMMAND + 1 }
5267     };
5268     /* *INDENT-ON* */
5269
5270     static int mode = REQ_INS_MODE;
5271     int c = wGetchar(w);
5272     unsigned n;
5273     FIELD *me = current_field(f);
5274     bool current = TRUE;
5275
5276     if (c == CTRL(']')) {
5277         if (mode == REQ_INS_MODE) {
5278             mode = REQ_OVL_MODE;
5279         } else {
5280             mode = REQ_INS_MODE;
5281         }
5282         c = mode;
5283     } else {
5284         for (n = 0; n < SIZEOF(lookup); n++) {
5285             if (lookup[n].code == c) {
5286                 c = lookup[n].result;
5287                 break;
5288             }
5289         }
5290     }
5291     mvprintw(0, COLS - 6, "(%s)", mode == REQ_INS_MODE ? "INS" : "OVL");
5292
5293     /*
5294      * Force the field that the user is typing into to be in reverse video,
5295      * while the other fields are shown underlined.
5296      */
5297     switch (c) {
5298     case REQ_BEG_FIELD:
5299     case REQ_CLR_EOF:
5300     case REQ_CLR_EOL:
5301     case REQ_CLR_FIELD:
5302     case REQ_DEL_CHAR:
5303     case REQ_DEL_LINE:
5304     case REQ_DEL_PREV:
5305     case REQ_DEL_WORD:
5306     case REQ_END_FIELD:
5307     case REQ_INS_CHAR:
5308     case REQ_INS_LINE:
5309     case REQ_LEFT_CHAR:
5310     case REQ_LEFT_FIELD:
5311     case REQ_NEXT_WORD:
5312     case REQ_RIGHT_CHAR:
5313         current = TRUE;
5314         break;
5315     default:
5316         current = (c < KEY_MAX);
5317         break;
5318     }
5319     if (current) {
5320         c = edit_secure(me, c);
5321         set_field_back(me, A_REVERSE);
5322     } else {
5323         c = edit_secure(me, c);
5324         set_field_back(me, A_UNDERLINE);
5325     }
5326     return c;
5327 }
5328
5329 static int
5330 my_form_driver(FORM * form, int c)
5331 {
5332     if (c == (MAX_FORM_COMMAND + 1)
5333         && form_driver(form, REQ_VALIDATION) == E_OK)
5334         return (TRUE);
5335     else {
5336         beep();
5337         return (FALSE);
5338     }
5339 }
5340
5341 #ifdef NCURSES_VERSION
5342 #define FIELDCHECK_CB(func) bool func(FIELD * fld, const void * data GCC_UNUSED)
5343 #define CHAR_CHECK_CB(func) bool func(int ch, const void *data GCC_UNUSED)
5344 #else
5345 #define FIELDCHECK_CB(func) int func(FIELD * fld, char * data GCC_UNUSED)
5346 #define CHAR_CHECK_CB(func) int func(int ch, char *data GCC_UNUSED)
5347 #endif
5348
5349 /*
5350  * Allow a middle initial, optionally with a '.' to end it.
5351  */
5352 static
5353 FIELDCHECK_CB(mi_field_check)
5354 {
5355     char *s = field_buffer(fld, 0);
5356     int state = 0;
5357     int n;
5358
5359     for (n = 0; s[n] != '\0'; ++n) {
5360         switch (state) {
5361         case 0:
5362             if (s[n] == '.') {
5363                 if (n != 1)
5364                     return FALSE;
5365                 state = 2;
5366             } else if (isspace(UChar(s[n]))) {
5367                 state = 2;
5368             }
5369             break;
5370         case 2:
5371             if (!isspace(UChar(s[n])))
5372                 return FALSE;
5373             break;
5374         }
5375     }
5376
5377     /* force the form to display a leading capital */
5378     if (islower(UChar(s[0]))) {
5379         s[0] = toupper(UChar(s[0]));
5380         set_field_buffer(fld, 0, s);
5381     }
5382     return TRUE;
5383 }
5384
5385 static
5386 CHAR_CHECK_CB(mi_char_check)
5387 {
5388     return ((isalpha(ch) || ch == '.') ? TRUE : FALSE);
5389 }
5390
5391 /*
5392  * Passwords should be at least 6 characters.
5393  */
5394 static
5395 FIELDCHECK_CB(pw_field_check)
5396 {
5397     char *s = field_buffer(fld, 0);
5398     int n;
5399
5400     for (n = 0; s[n] != '\0'; ++n) {
5401         if (isspace(UChar(s[n]))) {
5402             if (n < 6)
5403                 return FALSE;
5404         }
5405     }
5406     return TRUE;
5407 }
5408
5409 static
5410 CHAR_CHECK_CB(pw_char_check)
5411 {
5412     return (isgraph(ch) ? TRUE : FALSE);
5413 }
5414
5415 static void
5416 demo_forms(void)
5417 {
5418     WINDOW *w;
5419     FORM *form;
5420     FIELD *f[12], *secure;
5421     FIELDTYPE *fty_middle = new_fieldtype(mi_field_check, mi_char_check);
5422     FIELDTYPE *fty_passwd = new_fieldtype(pw_field_check, pw_char_check);
5423     int finished = 0, c;
5424     unsigned n = 0;
5425
5426 #ifdef NCURSES_MOUSE_VERSION
5427     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
5428 #endif
5429
5430     move(18, 0);
5431     addstr("Defined edit/traversal keys:   ^Q/ESC- exit form\n");
5432     addstr("^N   -- go to next field       ^P  -- go to previous field\n");
5433     addstr("Home -- go to first field      End -- go to last field\n");
5434     addstr("^L   -- go to field to left    ^R  -- go to field to right\n");
5435     addstr("^U   -- move upward to field   ^D  -- move downward to field\n");
5436     addstr("^W   -- go to next word        ^B  -- go to previous word\n");
5437     addstr("^S   -- go to start of field   ^E  -- go to end of field\n");
5438     addstr("^H   -- delete previous char   ^Y  -- delete line\n");
5439     addstr("^G   -- delete current word    ^C  -- clear to end of line\n");
5440     addstr("^K   -- clear to end of field  ^X  -- clear field\n");
5441     addstr("Arrow keys move within a field as you would expect. ^] toggles overlay mode.");
5442
5443     mvaddstr(4, 57, "Forms Entry Test");
5444
5445     refresh();
5446
5447     /* describe the form */
5448     f[n++] = make_label(0, 15, "Sample Form");
5449
5450     f[n++] = make_label(2, 0, "Last Name");
5451     f[n++] = make_field(3, 0, 1, 18, FALSE);
5452     set_field_type(f[n - 1], TYPE_ALPHA, 1);
5453
5454     f[n++] = make_label(2, 20, "First Name");
5455     f[n++] = make_field(3, 20, 1, 12, FALSE);
5456     set_field_type(f[n - 1], TYPE_ALPHA, 1);
5457
5458     f[n++] = make_label(2, 34, "Middle Name");
5459     f[n++] = make_field(3, 34, 1, 12, FALSE);
5460     set_field_type(f[n - 1], fty_middle);
5461
5462     f[n++] = make_label(5, 0, "Comments");
5463     f[n++] = make_field(6, 0, 4, 46, FALSE);
5464
5465     f[n++] = make_label(5, 20, "Password:");
5466     secure =
5467         f[n++] = make_field(5, 30, 1, 9, TRUE);
5468     set_field_type(f[n - 1], fty_passwd);
5469     f[n++] = (FIELD *) 0;
5470
5471     form = new_form(f);
5472
5473     display_form(form);
5474
5475     w = form_win(form);
5476     raw();
5477     nonl();                     /* lets us read ^M's */
5478     while (!finished) {
5479         switch (form_driver(form, c = form_virtualize(form, w))) {
5480         case E_OK:
5481             mvaddstr(5, 57, field_buffer(secure, 1));
5482             clrtoeol();
5483             refresh();
5484             break;
5485         case E_UNKNOWN_COMMAND:
5486             finished = my_form_driver(form, c);
5487             break;
5488         default:
5489             beep();
5490             break;
5491         }
5492     }
5493
5494     erase_form(form);
5495
5496     free_form(form);
5497     for (c = 0; f[c] != 0; c++)
5498         free_field(f[c]);
5499     free_fieldtype(fty_middle);
5500     free_fieldtype(fty_passwd);
5501     noraw();
5502     nl();
5503
5504 #ifdef NCURSES_MOUSE_VERSION
5505     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
5506 #endif
5507 }
5508 #endif /* USE_LIBFORM */
5509
5510 /****************************************************************************
5511  *
5512  * Overlap test
5513  *
5514  ****************************************************************************/
5515
5516 static void
5517 fillwin(WINDOW *win, char ch)
5518 {
5519     int y, x;
5520     int y1, x1;
5521
5522     getmaxyx(win, y1, x1);
5523     for (y = 0; y < y1; y++) {
5524         wmove(win, y, 0);
5525         for (x = 0; x < x1; x++)
5526             waddch(win, UChar(ch));
5527     }
5528 }
5529
5530 static void
5531 crosswin(WINDOW *win, char ch)
5532 {
5533     int y, x;
5534     int y1, x1;
5535
5536     getmaxyx(win, y1, x1);
5537     for (y = 0; y < y1; y++) {
5538         for (x = 0; x < x1; x++)
5539             if (((x > (x1 - 1) / 3) && (x <= (2 * (x1 - 1)) / 3))
5540                 || (((y > (y1 - 1) / 3) && (y <= (2 * (y1 - 1)) / 3)))) {
5541                 wmove(win, y, x);
5542                 waddch(win, UChar(ch));
5543             }
5544     }
5545 }
5546
5547 #define OVERLAP_FLAVORS 5
5548
5549 static void
5550 overlap_helpitem(int state, int item, char *message)
5551 {
5552     int row = (item / 2);
5553     int col = ((item % 2) ? COLS / 2 : 0);
5554
5555     move(LINES - 6 + row, col);
5556     printw("%c%c = %s", state == row ? '>' : ' ', 'a' + item, message);
5557     clrtoeol();
5558 }
5559
5560 static void
5561 overlap_test_1_attr(WINDOW *win, int flavor, int col)
5562 {
5563     short cpair = 1 + (flavor * 2) + col;
5564
5565     switch (flavor) {
5566     case 0:
5567         wattrset(win, A_NORMAL);
5568         break;
5569     case 1:
5570         wattrset(win, A_BOLD);
5571         break;
5572     case 2:
5573         init_pair(cpair, COLOR_BLUE, COLOR_WHITE);
5574         wattrset(win, COLOR_PAIR(cpair) | A_NORMAL);
5575         break;
5576     case 3:
5577         init_pair(cpair, COLOR_WHITE, COLOR_BLUE);
5578         wattrset(win, COLOR_PAIR(cpair) | A_BOLD);
5579         break;
5580     }
5581 }
5582
5583 static void
5584 overlap_test_2_attr(WINDOW *win, int flavor, int col)
5585 {
5586     short cpair = 9 + (flavor * 2) + col;
5587
5588     switch (flavor) {
5589     case 0:
5590         /* no effect */
5591         break;
5592     case 1:
5593         /* no effect */
5594         break;
5595     case 2:
5596         init_pair(cpair, COLOR_RED, COLOR_GREEN);
5597         wbkgdset(win, ' ' | A_BLINK | COLOR_PAIR(cpair));
5598         break;
5599     case 3:
5600         wbkgdset(win, ' ' | A_NORMAL);
5601         break;
5602     }
5603 }
5604
5605 static int
5606 overlap_help(int state, int flavors[OVERLAP_FLAVORS])
5607 {
5608     int row;
5609     int col;
5610     int item;
5611     const char *ths, *tht;
5612     char msg[80];
5613
5614     if (state < 0)
5615         state += OVERLAP_FLAVORS;
5616     state = state % OVERLAP_FLAVORS;
5617
5618     for (item = 0; item < (2 * OVERLAP_FLAVORS); ++item) {
5619         row = item / 2;
5620         col = item % 2;
5621         ths = col ? "B" : "A";
5622         tht = col ? "A" : "B";
5623
5624         switch (row) {
5625         case 0:
5626             flavors[row] = 0;
5627             sprintf(msg, "refresh %s, then %s, then doupdate.", ths, tht);
5628             break;
5629         case 1:
5630             if (use_colors) {
5631                 flavors[row] %= 4;
5632             } else {
5633                 flavors[row] %= 2;
5634             }
5635             overlap_test_1_attr(stdscr, flavors[row], col);
5636             sprintf(msg, "fill window %s with letter %s.", ths, ths);
5637             break;
5638         case 2:
5639             if (use_colors) {
5640                 flavors[row] %= 4;
5641             } else {
5642                 flavors[row] %= 2;
5643             }
5644             switch (flavors[row]) {
5645             case 0:
5646                 sprintf(msg, "cross pattern in window %s.", ths);
5647                 break;
5648             case 1:
5649                 sprintf(msg, "draw box in window %s.", ths);
5650                 break;
5651             case 2:
5652                 sprintf(msg, "set background of window %s.", ths);
5653                 break;
5654             case 3:
5655                 sprintf(msg, "reset background of window %s.", ths);
5656                 break;
5657             }
5658             break;
5659         case 3:
5660             flavors[row] = 0;
5661             sprintf(msg, "clear window %s.", ths);
5662             break;
5663         case 4:
5664             flavors[row] %= 4;
5665             switch (flavors[row]) {
5666             case 0:
5667                 sprintf(msg, "overwrite %s onto %s.", ths, tht);
5668                 break;
5669             case 1:
5670                 sprintf(msg, "copywin(FALSE) %s onto %s.", ths, tht);
5671                 break;
5672             case 2:
5673                 sprintf(msg, "copywin(TRUE) %s onto %s.", ths, tht);
5674                 break;
5675             case 3:
5676                 sprintf(msg, "overlay %s onto %s.", ths, tht);
5677                 break;
5678             }
5679             break;
5680         }
5681         overlap_helpitem(state, item, msg);
5682         wattrset(stdscr, A_NORMAL);
5683         wbkgdset(stdscr, ' ' | A_NORMAL);
5684     }
5685     move(LINES - 1, 0);
5686     printw("^Q/ESC = terminate test.  Up/down/space select test variations (%d %d).",
5687            state, flavors[state]);
5688
5689     return state;
5690 }
5691
5692 static void
5693 overlap_test_0(WINDOW *a, WINDOW *b)
5694 {
5695     touchwin(a);
5696     touchwin(b);
5697     wnoutrefresh(a);
5698     wnoutrefresh(b);
5699     doupdate();
5700 }
5701
5702 static void
5703 overlap_test_1(int flavor, int col, WINDOW *a, char fill)
5704 {
5705     overlap_test_1_attr(a, flavor, col);
5706     fillwin(a, fill);
5707     wattrset(a, A_NORMAL);
5708 }
5709
5710 static void
5711 overlap_test_2(int flavor, int col, WINDOW *a, char fill)
5712 {
5713     overlap_test_2_attr(a, flavor, col);
5714     switch (flavor) {
5715     case 0:
5716         crosswin(a, fill);
5717         break;
5718     case 1:
5719         box(a, 0, 0);
5720         break;
5721     case 2:
5722         /* done in overlap_test_2_attr */
5723         break;
5724     case 3:
5725         /* done in overlap_test_2_attr */
5726         break;
5727     }
5728 }
5729
5730 static void
5731 overlap_test_3(WINDOW *a)
5732 {
5733     wclear(a);
5734     wmove(a, 0, 0);
5735 }
5736
5737 static void
5738 overlap_test_4(int flavor, WINDOW *a, WINDOW *b)
5739 {
5740     switch (flavor) {
5741     case 0:
5742         overwrite(a, b);
5743         break;
5744     case 1:
5745         copywin(a, b, 0, 0, 0, 0, getmaxy(b), getmaxx(b), FALSE);
5746         break;
5747     case 2:
5748         copywin(a, b, 0, 0, 0, 0, getmaxy(b), getmaxx(b), TRUE);
5749         break;
5750     case 3:
5751         overlay(a, b);
5752         break;
5753     }
5754 }
5755
5756 /* test effects of overlapping windows */
5757 static void
5758 overlap_test(void)
5759 {
5760     int ch;
5761     int state, flavor[OVERLAP_FLAVORS];
5762
5763     WINDOW *win1 = newwin(9, 20, 3, 3);
5764     WINDOW *win2 = newwin(9, 20, 9, 16);
5765
5766     curs_set(0);
5767     raw();
5768     refresh();
5769     move(0, 0);
5770     printw("This test shows the behavior of wnoutrefresh() with respect to\n");
5771     printw("the shared region of two overlapping windows A and B.  The cross\n");
5772     printw("pattern in each window does not overlap the other.\n");
5773
5774     memset(flavor, 0, sizeof(flavor));
5775     state = overlap_help(0, flavor);
5776
5777     while (!isQuit(ch = Getchar()))
5778         switch (ch) {
5779         case 'a':               /* refresh window A first, then B */
5780             overlap_test_0(win1, win2);
5781             break;
5782
5783         case 'b':               /* refresh window B first, then A */
5784             overlap_test_0(win2, win1);
5785             break;
5786
5787         case 'c':               /* fill window A so it's visible */
5788             overlap_test_1(flavor[1], 0, win1, 'A');
5789             break;
5790
5791         case 'd':               /* fill window B so it's visible */
5792             overlap_test_1(flavor[1], 1, win2, 'B');
5793             break;
5794
5795         case 'e':               /* cross test pattern in window A */
5796             overlap_test_2(flavor[2], 0, win1, 'A');
5797             break;
5798
5799         case 'f':               /* cross test pattern in window A */
5800             overlap_test_2(flavor[2], 1, win2, 'B');
5801             break;
5802
5803         case 'g':               /* clear window A */
5804             overlap_test_3(win1);
5805             break;
5806
5807         case 'h':               /* clear window B */
5808             overlap_test_3(win2);
5809             break;
5810
5811         case 'i':               /* overwrite A onto B */
5812             overlap_test_4(flavor[4], win1, win2);
5813             break;
5814
5815         case 'j':               /* overwrite B onto A */
5816             overlap_test_4(flavor[4], win2, win1);
5817             break;
5818
5819         case CTRL('n'):
5820         case KEY_DOWN:
5821             state = overlap_help(state + 1, flavor);
5822             break;
5823
5824         case CTRL('p'):
5825         case KEY_UP:
5826             state = overlap_help(state - 1, flavor);
5827             break;
5828
5829         case ' ':
5830             flavor[state] += 1;
5831             state = overlap_help(state, flavor);
5832             break;
5833
5834         case '?':
5835             state = overlap_help(state, flavor);
5836             break;
5837
5838         default:
5839             beep();
5840             break;
5841         }
5842
5843     delwin(win2);
5844     delwin(win1);
5845     erase();
5846     curs_set(1);
5847     endwin();
5848 }
5849
5850 /****************************************************************************
5851  *
5852  * Main sequence
5853  *
5854  ****************************************************************************/
5855
5856 static bool
5857 do_single_test(const char c)
5858 /* perform a single specified test */
5859 {
5860     switch (c) {
5861     case 'a':
5862         getch_test();
5863         break;
5864
5865 #if USE_WIDEC_SUPPORT
5866     case 'A':
5867         get_wch_test();
5868         break;
5869 #endif
5870
5871     case 'b':
5872         attr_test();
5873         break;
5874
5875 #if USE_WIDEC_SUPPORT
5876     case 'B':
5877         wide_attr_test();
5878         break;
5879 #endif
5880
5881     case 'c':
5882         if (!use_colors)
5883             Cannot("does not support color.");
5884         else
5885             color_test();
5886         break;
5887
5888 #if USE_WIDEC_SUPPORT
5889     case 'C':
5890         if (!use_colors)
5891             Cannot("does not support color.");
5892         else
5893             wide_color_test();
5894         break;
5895 #endif
5896
5897     case 'd':
5898         if (!use_colors)
5899             Cannot("does not support color.");
5900         else if (!can_change_color())
5901             Cannot("has hardwired color values.");
5902         else
5903             color_edit();
5904         break;
5905
5906 #if USE_SOFTKEYS
5907     case 'e':
5908         slk_test();
5909         break;
5910 #endif
5911
5912 #if USE_WIDEC_SUPPORT
5913     case 'E':
5914         wide_slk_test();
5915         break;
5916 #endif
5917     case 'f':
5918         acs_display();
5919         break;
5920
5921 #if USE_WIDEC_SUPPORT
5922     case 'F':
5923         wide_acs_display();
5924         break;
5925 #endif
5926
5927 #if USE_LIBPANEL
5928     case 'o':
5929         demo_panels(init_panel, fill_panel);
5930         break;
5931 #endif
5932
5933 #if USE_WIDEC_SUPPORT && USE_LIBPANEL
5934     case 'O':
5935         demo_panels(init_wide_panel, fill_wide_panel);
5936         break;
5937 #endif
5938
5939     case 'g':
5940         acs_and_scroll();
5941         break;
5942
5943     case 'i':
5944         flushinp_test(stdscr);
5945         break;
5946
5947     case 'k':
5948         test_sgr_attributes();
5949         break;
5950
5951 #if USE_LIBMENU
5952     case 'm':
5953         menu_test();
5954         break;
5955 #endif
5956
5957     case 'p':
5958         demo_pad();
5959         break;
5960
5961 #if USE_LIBFORM
5962     case 'r':
5963         demo_forms();
5964         break;
5965 #endif
5966
5967     case 's':
5968         overlap_test();
5969         break;
5970
5971 #if USE_LIBMENU && defined(TRACE)
5972     case 't':
5973         trace_set();
5974         break;
5975 #endif
5976
5977     case '?':
5978         break;
5979
5980     default:
5981         return FALSE;
5982     }
5983
5984     return TRUE;
5985 }
5986
5987 static void
5988 usage(void)
5989 {
5990     static const char *const tbl[] =
5991     {
5992         "Usage: ncurses [options]"
5993         ,""
5994         ,"Options:"
5995 #ifdef NCURSES_VERSION
5996         ,"  -a f,b   set default-colors (assumed white-on-black)"
5997         ,"  -d       use default-colors if terminal supports them"
5998 #endif
5999 #if USE_SOFTKEYS
6000         ,"  -e fmt   specify format for soft-keys test (e)"
6001 #endif
6002 #if HAVE_RIPOFFLINE
6003         ,"  -f       rip-off footer line (can repeat)"
6004         ,"  -h       rip-off header line (can repeat)"
6005 #endif
6006         ,"  -m       do not use colors"
6007         ,"  -p file  rgb values to use in 'd' rather than ncurses's builtin"
6008 #if USE_LIBPANEL
6009         ,"  -s msec  specify nominal time for panel-demo (default: 1, to hold)"
6010 #endif
6011 #ifdef TRACE
6012         ,"  -t mask  specify default trace-level (may toggle with ^T)"
6013 #endif
6014     };
6015     size_t n;
6016     for (n = 0; n < SIZEOF(tbl); n++)
6017         fprintf(stderr, "%s\n", tbl[n]);
6018     ExitProgram(EXIT_FAILURE);
6019 }
6020
6021 static void
6022 set_terminal_modes(void)
6023 {
6024     noraw();
6025     cbreak();
6026     noecho();
6027     scrollok(stdscr, TRUE);
6028     idlok(stdscr, TRUE);
6029     keypad(stdscr, TRUE);
6030 }
6031
6032 #ifdef SIGUSR1
6033 static RETSIGTYPE
6034 announce_sig(int sig)
6035 {
6036     (void) fprintf(stderr, "Handled signal %d\r\n", sig);
6037 }
6038 #endif
6039
6040 #if HAVE_RIPOFFLINE
6041 static int
6042 rip_footer(WINDOW *win, int cols)
6043 {
6044     wbkgd(win, A_REVERSE);
6045     werase(win);
6046     wmove(win, 0, 0);
6047     wprintw(win, "footer: window %p, %d columns", win, cols);
6048     wnoutrefresh(win);
6049     return OK;
6050 }
6051
6052 static int
6053 rip_header(WINDOW *win, int cols)
6054 {
6055     wbkgd(win, A_REVERSE);
6056     werase(win);
6057     wmove(win, 0, 0);
6058     wprintw(win, "header: window %p, %d columns", win, cols);
6059     wnoutrefresh(win);
6060     return OK;
6061 }
6062 #endif /* HAVE_RIPOFFLINE */
6063
6064 static void
6065 main_menu(bool top)
6066 {
6067     char command;
6068
6069     do {
6070         (void) puts("This is the ncurses main menu");
6071         (void) puts("a = keyboard and mouse input test");
6072 #if USE_WIDEC_SUPPORT
6073         (void) puts("A = wide-character keyboard and mouse input test");
6074 #endif
6075         (void) puts("b = character attribute test");
6076 #if USE_WIDEC_SUPPORT
6077         (void) puts("B = wide-character attribute test");
6078 #endif
6079         (void) puts("c = color test pattern");
6080 #if USE_WIDEC_SUPPORT
6081         (void) puts("C = color test pattern using wide-character calls");
6082 #endif
6083         if (top)
6084             (void) puts("d = edit RGB color values");
6085 #if USE_SOFTKEYS
6086         (void) puts("e = exercise soft keys");
6087 #if USE_WIDEC_SUPPORT
6088         (void) puts("E = exercise soft keys using wide-characters");
6089 #endif
6090 #endif
6091         (void) puts("f = display ACS characters");
6092 #if USE_WIDEC_SUPPORT
6093         (void) puts("F = display Wide-ACS characters");
6094 #endif
6095         (void) puts("g = display windows and scrolling");
6096         (void) puts("i = test of flushinp()");
6097         (void) puts("k = display character attributes");
6098 #if USE_LIBMENU
6099         (void) puts("m = menu code test");
6100 #endif
6101 #if USE_LIBPANEL
6102         (void) puts("o = exercise panels library");
6103 #if USE_WIDEC_SUPPORT
6104         (void) puts("O = exercise panels with wide-characters");
6105 #endif
6106 #endif
6107         (void) puts("p = exercise pad features");
6108         (void) puts("q = quit");
6109 #if USE_LIBFORM
6110         (void) puts("r = exercise forms code");
6111 #endif
6112         (void) puts("s = overlapping-refresh test");
6113 #if USE_LIBMENU && defined(TRACE)
6114         (void) puts("t = set trace level");
6115 #endif
6116         (void) puts("? = repeat this command summary");
6117
6118         (void) fputs("> ", stdout);
6119         (void) fflush(stdout);  /* necessary under SVr4 curses */
6120
6121         /*
6122          * This used to be an 'fgets()' call.  However (on Linux, at least)
6123          * mixing stream I/O and 'read()' (used in the library) causes the
6124          * input stream to be flushed when switching between the two.
6125          */
6126         command = 0;
6127         for (;;) {
6128             char ch;
6129             if (read(fileno(stdin), &ch, 1) <= 0) {
6130                 if (command == 0)
6131                     command = 'q';
6132                 break;
6133             } else if (command == 0 && !isspace(UChar(ch))) {
6134                 command = ch;
6135             } else if (ch == '\n' || ch == '\r') {
6136                 if ((command == 'd') && !top) {
6137                     (void) fputs("Do not nest test-d\n", stdout);
6138                     command = 0;
6139                 }
6140                 if (command != 0)
6141                     break;
6142                 (void) fputs("> ", stdout);
6143                 (void) fflush(stdout);
6144             }
6145         }
6146
6147         if (do_single_test(command)) {
6148             /*
6149              * This may be overkill; it's intended to reset everything back
6150              * to the initial terminal modes so that tests don't get in
6151              * each other's way.
6152              */
6153             flushinp();
6154             set_terminal_modes();
6155             reset_prog_mode();
6156             clear();
6157             refresh();
6158             endwin();
6159             if (command == '?') {
6160                 (void) puts("This is the ncurses capability tester.");
6161                 (void)
6162                     puts("You may select a test from the main menu by typing the");
6163                 (void)
6164                     puts("key letter of the choice (the letter to left of the =)");
6165                 (void)
6166                     puts("at the > prompt.  Type `q' to exit.");
6167             }
6168             continue;
6169         }
6170     } while
6171         (command != 'q');
6172 }
6173
6174 /*+-------------------------------------------------------------------------
6175         main(argc,argv)
6176 --------------------------------------------------------------------------*/
6177
6178 #define okCOLOR(n) ((n) >= 0 && (n) < max_colors)
6179 #define okRGB(n)   ((n) >= 0 && (n) <= 1000)
6180
6181 int
6182 main(int argc, char *argv[])
6183 {
6184     int c;
6185     int my_e_param = 1;
6186 #ifdef NCURSES_VERSION
6187     int default_fg = COLOR_WHITE;
6188     int default_bg = COLOR_BLACK;
6189     bool assumed_colors = FALSE;
6190     bool default_colors = FALSE;
6191 #endif
6192     char *palette_file = 0;
6193     bool monochrome = FALSE;
6194
6195     setlocale(LC_ALL, "");
6196
6197     while ((c = getopt(argc, argv, "a:de:fhmp:s:t:")) != -1) {
6198         switch (c) {
6199 #ifdef NCURSES_VERSION
6200         case 'a':
6201             assumed_colors = TRUE;
6202             sscanf(optarg, "%d,%d", &default_fg, &default_bg);
6203             break;
6204         case 'd':
6205             default_colors = TRUE;
6206             break;
6207 #endif
6208         case 'e':
6209             my_e_param = atoi(optarg);
6210 #ifdef NCURSES_VERSION
6211             if (my_e_param > 3) /* allow extended layouts */
6212                 usage();
6213 #else
6214             if (my_e_param > 1)
6215                 usage();
6216 #endif
6217             break;
6218 #if HAVE_RIPOFFLINE
6219         case 'f':
6220             ripoffline(-1, rip_footer);
6221             break;
6222         case 'h':
6223             ripoffline(1, rip_header);
6224             break;
6225 #endif /* HAVE_RIPOFFLINE */
6226         case 'm':
6227             monochrome = TRUE;
6228             break;
6229         case 'p':
6230             palette_file = optarg;
6231             break;
6232 #if USE_LIBPANEL
6233         case 's':
6234             nap_msec = atol(optarg);
6235             break;
6236 #endif
6237 #ifdef TRACE
6238         case 't':
6239             save_trace = strtol(optarg, 0, 0);
6240             break;
6241 #endif
6242         default:
6243             usage();
6244         }
6245     }
6246
6247     /*
6248      * If there's no menus (unlikely for ncurses!), then we'll have to set
6249      * tracing on initially, just in case the user wants to test something that
6250      * doesn't involve wGetchar.
6251      */
6252 #ifdef TRACE
6253     /* enable debugging */
6254 #if !USE_LIBMENU
6255     trace(save_trace);
6256 #else
6257     if (!isatty(fileno(stdin)))
6258         trace(save_trace);
6259 #endif /* USE_LIBMENU */
6260 #endif /* TRACE */
6261
6262 #if USE_SOFTKEYS
6263     /* tell it we're going to play with soft keys */
6264     slk_init(my_e_param);
6265 #endif
6266
6267 #ifdef SIGUSR1
6268     /* set up null signal catcher so we can see what interrupts to getch do */
6269     signal(SIGUSR1, announce_sig);
6270 #endif
6271
6272     /* we must initialize the curses data structure only once */
6273     initscr();
6274     bkgdset(BLANK);
6275
6276     /* tests, in general, will want these modes */
6277     use_colors = monochrome ? FALSE : has_colors();
6278
6279     if (use_colors) {
6280         start_color();
6281 #ifdef NCURSES_VERSION_PATCH
6282         max_colors = COLORS;    /* was > 16 ? 16 : COLORS */
6283 #if HAVE_USE_DEFAULT_COLORS
6284         if (default_colors) {
6285             use_default_colors();
6286             min_colors = -1;
6287         }
6288 #if NCURSES_VERSION_PATCH >= 20000708
6289         else if (assumed_colors)
6290             assume_default_colors(default_fg, default_bg);
6291 #endif
6292 #endif
6293 #else /* normal SVr4 curses */
6294         max_colors = COLORS;    /* was > 8 ? 8 : COLORS */
6295 #endif
6296         max_pairs = COLOR_PAIRS;        /* was > 256 ? 256 : COLOR_PAIRS */
6297
6298         if (can_change_color()) {
6299             short cp;
6300             all_colors = (RGB_DATA *) malloc(max_colors * sizeof(RGB_DATA));
6301             for (cp = 0; cp < max_colors; ++cp) {
6302                 color_content(cp,
6303                               &all_colors[cp].red,
6304                               &all_colors[cp].green,
6305                               &all_colors[cp].blue);
6306             }
6307             if (palette_file != 0) {
6308                 FILE *fp = fopen(palette_file, "r");
6309                 if (fp != 0) {
6310                     char buffer[BUFSIZ];
6311                     int red, green, blue;
6312                     int scale = 1000;
6313                     while (fgets(buffer, sizeof(buffer), fp) != 0) {
6314                         if (sscanf(buffer, "scale:%d", &c) == 1) {
6315                             scale = c;
6316                         } else if (sscanf(buffer, "%d:%d %d %d",
6317                                           &c,
6318                                           &red,
6319                                           &green,
6320                                           &blue) == 4
6321                                    && okCOLOR(c)
6322                                    && okRGB(red)
6323                                    && okRGB(green)
6324                                    && okRGB(blue)) {
6325                             all_colors[c].red = (red * 1000) / scale;
6326                             all_colors[c].green = (green * 1000) / scale;
6327                             all_colors[c].blue = (blue * 1000) / scale;
6328                         }
6329                     }
6330                     fclose(fp);
6331                 }
6332             }
6333         }
6334     }
6335     set_terminal_modes();
6336     def_prog_mode();
6337
6338     /*
6339      * Return to terminal mode, so we're guaranteed of being able to
6340      * select terminal commands even if the capabilities are wrong.
6341      */
6342     endwin();
6343
6344 #if HAVE_CURSES_VERSION
6345     (void) printf("Welcome to %s.  Press ? for help.\n", curses_version());
6346 #elif defined(NCURSES_VERSION_MAJOR) && defined(NCURSES_VERSION_MINOR) && defined(NCURSES_VERSION_PATCH)
6347     (void) printf("Welcome to ncurses %d.%d.%d.  Press ? for help.\n",
6348                   NCURSES_VERSION_MAJOR,
6349                   NCURSES_VERSION_MINOR,
6350                   NCURSES_VERSION_PATCH);
6351 #else
6352     (void) puts("Welcome to ncurses.  Press ? for help.");
6353 #endif
6354
6355     main_menu(TRUE);
6356
6357     ExitProgram(EXIT_SUCCESS);
6358 }
6359
6360 /* ncurses.c ends here */