]> ncurses.scripts.mit.edu Git - ncurses.git/blob - test/ncurses.c
50f24947f45af39f37847e363d91e89bc6755764
[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.298 2008/01/12 23:06:58 tom Exp $
44
45 ***************************************************************************/
46
47 #include <test.priv.h>
48
49 #ifdef __hpux
50 #undef mvwdelch                 /* HPUX 11.23 macro will not compile */
51 #endif
52
53 #if HAVE_GETTIMEOFDAY
54 #if HAVE_SYS_TIME_H && HAVE_SYS_TIME_SELECT
55 #include <sys/time.h>
56 #endif
57 #if HAVE_SYS_SELECT_H
58 #include <sys/select.h>
59 #endif
60 #endif
61
62 #if USE_LIBPANEL
63 #include <panel.h>
64 #endif
65
66 #if USE_LIBMENU
67 #include <menu.h>
68 #endif
69
70 #if USE_LIBFORM
71 #include <form.h>
72 #endif
73
74 #ifdef NCURSES_VERSION
75
76 #define NCURSES_CONST_PARAM const void
77
78 #ifdef TRACE
79 static unsigned save_trace = TRACE_ORDINARY | TRACE_ICALLS | TRACE_CALLS;
80 extern unsigned _nc_tracing;
81 #endif
82
83 #else
84
85 #define NCURSES_CONST_PARAM char
86
87 #define mmask_t chtype          /* not specified in XSI */
88
89 #ifndef ACS_S3
90 #ifdef CURSES_ACS_ARRAY
91 #define ACS_S3          (CURSES_ACS_ARRAY['p'])         /* scan line 3 */
92 #define ACS_S7          (CURSES_ACS_ARRAY['r'])         /* scan line 7 */
93 #define ACS_LEQUAL      (CURSES_ACS_ARRAY['y'])         /* less/equal */
94 #define ACS_GEQUAL      (CURSES_ACS_ARRAY['z'])         /* greater/equal */
95 #define ACS_PI          (CURSES_ACS_ARRAY['{'])         /* Pi */
96 #define ACS_NEQUAL      (CURSES_ACS_ARRAY['|'])         /* not equal */
97 #define ACS_STERLING    (CURSES_ACS_ARRAY['}'])         /* UK pound sign */
98 #else
99 #define ACS_S3          (A_ALTCHARSET + 'p')    /* scan line 3 */
100 #define ACS_S7          (A_ALTCHARSET + 'r')    /* scan line 7 */
101 #define ACS_LEQUAL      (A_ALTCHARSET + 'y')    /* less/equal */
102 #define ACS_GEQUAL      (A_ALTCHARSET + 'z')    /* greater/equal */
103 #define ACS_PI          (A_ALTCHARSET + '{')    /* Pi */
104 #define ACS_NEQUAL      (A_ALTCHARSET + '|')    /* not equal */
105 #define ACS_STERLING    (A_ALTCHARSET + '}')    /* UK pound sign */
106 #endif
107 #endif /* ACS_S3 */
108
109 #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             _tracef("TOGGLE-TRACING OFF");
187             _nc_tracing = 0;
188         } else {
189             _nc_tracing = save_trace;
190         }
191         trace(_nc_tracing);
192         if (_nc_tracing)
193             _tracef("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             _tracef("TOGGLE-TRACING OFF");
280             _nc_tracing = 0;
281         } else {
282             _nc_tracing = save_trace;
283         }
284         trace(_nc_tracing);
285         if (_nc_tracing)
286             _tracef("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)
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 #ifdef KEY_RESIZE
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 #ifdef KEY_RESIZE
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     attr_t                      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, 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     attr_t 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
3399 #define is_keypad(win)   (win)->_use_keypad
3400 #define is_scrollok(win) (win)->_scroll
3401 #endif
3402 #else
3403 #define is_keypad(win)   FALSE
3404 #define is_scrollok(win) FALSE
3405 #endif
3406
3407 /* We need to know if these flags are actually set, so don't look in FRAME.
3408  * These names are known to work with SVr4 curses as well as ncurses.  The
3409  * _use_keypad name does not work with Solaris 8.
3410  */
3411 static bool
3412 HaveKeypad(FRAME * curp)
3413 {
3414     WINDOW *win = (curp ? curp->wind : stdscr);
3415     (void) win;
3416     return is_keypad(win);
3417 }
3418
3419 static bool
3420 HaveScroll(FRAME * curp)
3421 {
3422     WINDOW *win = (curp ? curp->wind : stdscr);
3423     (void) win;
3424     return is_scrollok(win);
3425 }
3426
3427 static void
3428 newwin_legend(FRAME * curp)
3429 {
3430     static const struct {
3431         const char *msg;
3432         int code;
3433     } legend[] = {
3434         {
3435             "^C = create window", 0
3436         },
3437         {
3438             "^N = next window", 0
3439         },
3440         {
3441             "^P = previous window", 0
3442         },
3443         {
3444             "^F = scroll forward", 0
3445         },
3446         {
3447             "^B = scroll backward", 0
3448         },
3449         {
3450             "^K = keypad(%s)", 1
3451         },
3452         {
3453             "^S = scrollok(%s)", 2
3454         },
3455         {
3456             "^W = save window to file", 0
3457         },
3458         {
3459             "^R = restore window", 0
3460         },
3461 #if HAVE_WRESIZE
3462         {
3463             "^X = resize", 0
3464         },
3465 #endif
3466         {
3467             "^Q%s = exit", 3
3468         }
3469     };
3470     size_t n;
3471     int x;
3472     bool do_keypad = HaveKeypad(curp);
3473     bool do_scroll = HaveScroll(curp);
3474     char buf[BUFSIZ];
3475
3476     move(LINES - 4, 0);
3477     for (n = 0; n < SIZEOF(legend); n++) {
3478         switch (legend[n].code) {
3479         default:
3480             strcpy(buf, legend[n].msg);
3481             break;
3482         case 1:
3483             sprintf(buf, legend[n].msg, do_keypad ? "yes" : "no");
3484             break;
3485         case 2:
3486             sprintf(buf, legend[n].msg, do_scroll ? "yes" : "no");
3487             break;
3488         case 3:
3489             sprintf(buf, legend[n].msg, do_keypad ? "/ESC" : "");
3490             break;
3491         }
3492         x = getcurx(stdscr);
3493         addstr((COLS < (x + 3 + (int) strlen(buf))) ? "\n" : (n ? ", " : ""));
3494         addstr(buf);
3495     }
3496     clrtoeol();
3497 }
3498
3499 static void
3500 transient(FRAME * curp, NCURSES_CONST char *msg)
3501 {
3502     newwin_legend(curp);
3503     if (msg) {
3504         mvaddstr(LINES - 1, 0, msg);
3505         refresh();
3506         napms(1000);
3507     }
3508
3509     move(LINES - 1, 0);
3510     printw("%s characters are echoed, window should %sscroll.",
3511            HaveKeypad(curp) ? "Non-arrow" : "All other",
3512            HaveScroll(curp) ? "" : "not ");
3513     clrtoeol();
3514 }
3515
3516 static void
3517 newwin_report(FRAME * curp)
3518 /* report on the cursor's current position, then restore it */
3519 {
3520     WINDOW *win = (curp != 0) ? curp->wind : stdscr;
3521     int y, x;
3522
3523     if (win != stdscr)
3524         transient(curp, (char *) 0);
3525     getyx(win, y, x);
3526     move(LINES - 1, COLS - 17);
3527     printw("Y = %2d X = %2d", y, x);
3528     if (win != stdscr)
3529         refresh();
3530     else
3531         wmove(win, y, x);
3532 }
3533
3534 static pair *
3535 selectcell(int uli, int ulj, int lri, int lrj)
3536 /* arrows keys move cursor, return location at current on non-arrow key */
3537 {
3538     static pair res;            /* result cell */
3539     int si = lri - uli + 1;     /* depth of the select area */
3540     int sj = lrj - ulj + 1;     /* width of the select area */
3541     int i = 0, j = 0;           /* offsets into the select area */
3542
3543     res.y = uli;
3544     res.x = ulj;
3545     for (;;) {
3546         move(uli + i, ulj + j);
3547         newwin_report((FRAME *) 0);
3548
3549         switch (Getchar()) {
3550         case KEY_UP:
3551             i += si - 1;
3552             break;
3553         case KEY_DOWN:
3554             i++;
3555             break;
3556         case KEY_LEFT:
3557             j += sj - 1;
3558             break;
3559         case KEY_RIGHT:
3560             j++;
3561             break;
3562           case_QUIT:
3563             return ((pair *) 0);
3564 #ifdef NCURSES_MOUSE_VERSION
3565         case KEY_MOUSE:
3566             {
3567                 MEVENT event;
3568
3569                 getmouse(&event);
3570                 if (event.y > uli && event.x > ulj) {
3571                     i = event.y - uli;
3572                     j = event.x - ulj;
3573                 } else {
3574                     beep();
3575                     break;
3576                 }
3577             }
3578             /* FALLTHRU */
3579 #endif
3580         default:
3581             res.y = uli + i;
3582             res.x = ulj + j;
3583             return (&res);
3584         }
3585         i %= si;
3586         j %= sj;
3587     }
3588 }
3589
3590 static void
3591 outerbox(pair ul, pair lr, bool onoff)
3592 /* draw or erase a box *outside* the given pair of corners */
3593 {
3594     mvaddch(ul.y - 1, lr.x - 1, onoff ? ACS_ULCORNER : ' ');
3595     mvaddch(ul.y - 1, lr.x + 1, onoff ? ACS_URCORNER : ' ');
3596     mvaddch(lr.y + 1, lr.x + 1, onoff ? ACS_LRCORNER : ' ');
3597     mvaddch(lr.y + 1, ul.x - 1, onoff ? ACS_LLCORNER : ' ');
3598     move(ul.y - 1, ul.x);
3599     hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
3600     move(ul.y, ul.x - 1);
3601     vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
3602     move(lr.y + 1, ul.x);
3603     hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
3604     move(ul.y, lr.x + 1);
3605     vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
3606 }
3607
3608 static WINDOW *
3609 getwindow(void)
3610 /* Ask user for a window definition */
3611 {
3612     WINDOW *rwindow;
3613     pair ul, lr, *tmp;
3614
3615     move(0, 0);
3616     clrtoeol();
3617     addstr("Use arrows to move cursor, anything else to mark corner 1");
3618     refresh();
3619     if ((tmp = selectcell(2, 1, LINES - BOTLINES - 2, COLS - 2)) == (pair *) 0)
3620         return ((WINDOW *) 0);
3621     memcpy(&ul, tmp, sizeof(pair));
3622     mvaddch(ul.y - 1, ul.x - 1, ACS_ULCORNER);
3623     move(0, 0);
3624     clrtoeol();
3625     addstr("Use arrows to move cursor, anything else to mark corner 2");
3626     refresh();
3627     if ((tmp = selectcell(ul.y, ul.x, LINES - BOTLINES - 2, COLS - 2)) ==
3628         (pair *) 0)
3629         return ((WINDOW *) 0);
3630     memcpy(&lr, tmp, sizeof(pair));
3631
3632     rwindow = subwin(stdscr, lr.y - ul.y + 1, lr.x - ul.x + 1, ul.y, ul.x);
3633
3634     outerbox(ul, lr, TRUE);
3635     refresh();
3636
3637     wrefresh(rwindow);
3638
3639     move(0, 0);
3640     clrtoeol();
3641     return (rwindow);
3642 }
3643
3644 static void
3645 newwin_move(FRAME * curp, int dy, int dx)
3646 {
3647     WINDOW *win = (curp != 0) ? curp->wind : stdscr;
3648     int cur_y, cur_x;
3649     int max_y, max_x;
3650
3651     getyx(win, cur_y, cur_x);
3652     getmaxyx(win, max_y, max_x);
3653     if ((cur_x += dx) < 0)
3654         cur_x = 0;
3655     else if (cur_x >= max_x)
3656         cur_x = max_x - 1;
3657     if ((cur_y += dy) < 0)
3658         cur_y = 0;
3659     else if (cur_y >= max_y)
3660         cur_y = max_y - 1;
3661     wmove(win, cur_y, cur_x);
3662 }
3663
3664 static FRAME *
3665 delete_framed(FRAME * fp, bool showit)
3666 {
3667     FRAME *np;
3668
3669     fp->last->next = fp->next;
3670     fp->next->last = fp->last;
3671
3672     if (showit) {
3673         werase(fp->wind);
3674         wrefresh(fp->wind);
3675     }
3676     delwin(fp->wind);
3677
3678     np = (fp == fp->next) ? 0 : fp->next;
3679     free(fp);
3680     return np;
3681 }
3682
3683 static void
3684 acs_and_scroll(void)
3685 /* Demonstrate windows */
3686 {
3687     int c, i;
3688     FRAME *current = (FRAME *) 0, *neww;
3689     WINDOW *usescr = stdscr;
3690 #if HAVE_PUTWIN && HAVE_GETWIN
3691     FILE *fp;
3692 #endif
3693
3694 #define DUMPFILE        "screendump"
3695
3696 #ifdef NCURSES_MOUSE_VERSION
3697     mousemask(BUTTON1_CLICKED, (mmask_t *) 0);
3698 #endif
3699     c = CTRL('C');
3700     raw();
3701     do {
3702         transient((FRAME *) 0, (char *) 0);
3703         switch (c) {
3704         case CTRL('C'):
3705             neww = (FRAME *) calloc(1, sizeof(FRAME));
3706             if ((neww->wind = getwindow()) == (WINDOW *) 0)
3707                 goto breakout;
3708
3709             if (current == 0) { /* First element,  */
3710                 neww->next = neww;      /*   so point it at itself */
3711                 neww->last = neww;
3712             } else {
3713                 neww->next = current->next;
3714                 neww->last = current;
3715                 neww->last->next = neww;
3716                 neww->next->last = neww;
3717             }
3718             current = neww;
3719             /* SVr4 curses sets the keypad on all newly-created windows to
3720              * false.  Someone reported that PDCurses makes new windows inherit
3721              * this flag.  Remove the following 'keypad()' call to test this
3722              */
3723             keypad(current->wind, TRUE);
3724             current->do_keypad = HaveKeypad(current);
3725             current->do_scroll = HaveScroll(current);
3726             break;
3727
3728         case CTRL('N'): /* go to next window */
3729             if (current)
3730                 current = current->next;
3731             break;
3732
3733         case CTRL('P'): /* go to previous window */
3734             if (current)
3735                 current = current->last;
3736             break;
3737
3738         case CTRL('F'): /* scroll current window forward */
3739             if (current)
3740                 wscrl(current->wind, 1);
3741             break;
3742
3743         case CTRL('B'): /* scroll current window backwards */
3744             if (current)
3745                 wscrl(current->wind, -1);
3746             break;
3747
3748         case CTRL('K'): /* toggle keypad mode for current */
3749             if (current) {
3750                 current->do_keypad = !current->do_keypad;
3751                 keypad(current->wind, current->do_keypad);
3752             }
3753             break;
3754
3755         case CTRL('S'):
3756             if (current) {
3757                 current->do_scroll = !current->do_scroll;
3758                 scrollok(current->wind, current->do_scroll);
3759             }
3760             break;
3761
3762 #if HAVE_PUTWIN && HAVE_GETWIN
3763         case CTRL('W'): /* save and delete window */
3764             if (current == current->next) {
3765                 transient(current, "Will not save/delete ONLY window");
3766                 break;
3767             } else if ((fp = fopen(DUMPFILE, "w")) == (FILE *) 0) {
3768                 transient(current, "Can't open screen dump file");
3769             } else {
3770                 (void) putwin(current->wind, fp);
3771                 (void) fclose(fp);
3772
3773                 current = delete_framed(current, TRUE);
3774             }
3775             break;
3776
3777         case CTRL('R'): /* restore window */
3778             if ((fp = fopen(DUMPFILE, "r")) == (FILE *) 0) {
3779                 transient(current, "Can't open screen dump file");
3780             } else {
3781                 neww = (FRAME *) calloc(1, sizeof(FRAME));
3782
3783                 neww->next = current->next;
3784                 neww->last = current;
3785                 neww->last->next = neww;
3786                 neww->next->last = neww;
3787
3788                 neww->wind = getwin(fp);
3789                 (void) fclose(fp);
3790
3791                 wrefresh(neww->wind);
3792             }
3793             break;
3794 #endif
3795
3796 #if HAVE_WRESIZE
3797         case CTRL('X'): /* resize window */
3798             if (current) {
3799                 pair *tmp, ul, lr;
3800                 int mx, my;
3801
3802                 move(0, 0);
3803                 clrtoeol();
3804                 addstr("Use arrows to move cursor, anything else to mark new corner");
3805                 refresh();
3806
3807                 getbegyx(current->wind, ul.y, ul.x);
3808
3809                 tmp = selectcell(ul.y, ul.x, LINES - BOTLINES - 2, COLS - 2);
3810                 if (tmp == (pair *) 0) {
3811                     beep();
3812                     break;
3813                 }
3814
3815                 getmaxyx(current->wind, lr.y, lr.x);
3816                 lr.y += (ul.y - 1);
3817                 lr.x += (ul.x - 1);
3818                 outerbox(ul, lr, FALSE);
3819                 wnoutrefresh(stdscr);
3820
3821                 /* strictly cosmetic hack for the test */
3822                 getmaxyx(current->wind, my, mx);
3823                 if (my > tmp->y - ul.y) {
3824                     getyx(current->wind, lr.y, lr.x);
3825                     wmove(current->wind, tmp->y - ul.y + 1, 0);
3826                     wclrtobot(current->wind);
3827                     wmove(current->wind, lr.y, lr.x);
3828                 }
3829                 if (mx > tmp->x - ul.x)
3830                     for (i = 0; i < my; i++) {
3831                         wmove(current->wind, i, tmp->x - ul.x + 1);
3832                         wclrtoeol(current->wind);
3833                     }
3834                 wnoutrefresh(current->wind);
3835
3836                 memcpy(&lr, tmp, sizeof(pair));
3837                 (void) wresize(current->wind, lr.y - ul.y + 0, lr.x - ul.x + 0);
3838
3839                 getbegyx(current->wind, ul.y, ul.x);
3840                 getmaxyx(current->wind, lr.y, lr.x);
3841                 lr.y += (ul.y - 1);
3842                 lr.x += (ul.x - 1);
3843                 outerbox(ul, lr, TRUE);
3844                 wnoutrefresh(stdscr);
3845
3846                 wnoutrefresh(current->wind);
3847                 move(0, 0);
3848                 clrtoeol();
3849                 doupdate();
3850             }
3851             break;
3852 #endif /* HAVE_WRESIZE */
3853
3854         case KEY_F(10): /* undocumented --- use this to test area clears */
3855             selectcell(0, 0, LINES - 1, COLS - 1);
3856             clrtobot();
3857             refresh();
3858             break;
3859
3860         case KEY_UP:
3861             newwin_move(current, -1, 0);
3862             break;
3863         case KEY_DOWN:
3864             newwin_move(current, 1, 0);
3865             break;
3866         case KEY_LEFT:
3867             newwin_move(current, 0, -1);
3868             break;
3869         case KEY_RIGHT:
3870             newwin_move(current, 0, 1);
3871             break;
3872
3873         case KEY_BACKSPACE:
3874             /* FALLTHROUGH */
3875         case KEY_DC:
3876             {
3877                 int y, x;
3878                 getyx(current->wind, y, x);
3879                 if (--x < 0) {
3880                     if (--y < 0)
3881                         break;
3882                     x = getmaxx(current->wind) - 1;
3883                 }
3884                 mvwdelch(current->wind, y, x);
3885             }
3886             break;
3887
3888         case '\r':
3889             c = '\n';
3890             /* FALLTHROUGH */
3891
3892         default:
3893             if (current)
3894                 waddch(current->wind, (chtype) c);
3895             else
3896                 beep();
3897             break;
3898         }
3899         newwin_report(current);
3900         usescr = (current ? current->wind : stdscr);
3901         wrefresh(usescr);
3902     } while
3903         (!isQuit(c = wGetchar(usescr))
3904          && (c != ERR));
3905
3906   breakout:
3907     while (current != 0)
3908         current = delete_framed(current, FALSE);
3909
3910     scrollok(stdscr, TRUE);     /* reset to driver's default */
3911 #ifdef NCURSES_MOUSE_VERSION
3912     mousemask(0, (mmask_t *) 0);
3913 #endif
3914     noraw();
3915     erase();
3916     endwin();
3917 }
3918
3919 /****************************************************************************
3920  *
3921  * Panels tester
3922  *
3923  ****************************************************************************/
3924
3925 #if USE_LIBPANEL
3926 static int nap_msec = 1;
3927
3928 static NCURSES_CONST char *mod[] =
3929 {
3930     "test ",
3931     "TEST ",
3932     "(**) ",
3933     "*()* ",
3934     "<--> ",
3935     "LAST "
3936 };
3937
3938 /*+-------------------------------------------------------------------------
3939         wait_a_while(msec)
3940 --------------------------------------------------------------------------*/
3941 static void
3942 wait_a_while(int msec GCC_UNUSED)
3943 {
3944 #if HAVE_NAPMS
3945     if (nap_msec == 1)
3946         wGetchar(stdscr);
3947     else
3948         napms(nap_msec);
3949 #else
3950     if (nap_msec == 1)
3951         wGetchar(stdscr);
3952     else if (msec > 1000)
3953         sleep((unsigned) msec / 1000);
3954     else
3955         sleep(1);
3956 #endif
3957 }                               /* end of wait_a_while */
3958
3959 /*+-------------------------------------------------------------------------
3960         saywhat(text)
3961 --------------------------------------------------------------------------*/
3962 static void
3963 saywhat(NCURSES_CONST char *text)
3964 {
3965     wmove(stdscr, LINES - 1, 0);
3966     wclrtoeol(stdscr);
3967     if (text != 0 && *text != '\0') {
3968         waddstr(stdscr, text);
3969         waddstr(stdscr, "; ");
3970     }
3971     waddstr(stdscr, "press any key to continue");
3972 }                               /* end of saywhat */
3973
3974 /*+-------------------------------------------------------------------------
3975         mkpanel(rows,cols,tly,tlx) - alloc a win and panel and associate them
3976 --------------------------------------------------------------------------*/
3977 static PANEL *
3978 mkpanel(short color, int rows, int cols, int tly, int tlx)
3979 {
3980     WINDOW *win;
3981     PANEL *pan = 0;
3982
3983     if ((win = newwin(rows, cols, tly, tlx)) != 0) {
3984         if ((pan = new_panel(win)) == 0) {
3985             delwin(win);
3986         } else if (use_colors) {
3987             short fg = (color == COLOR_BLUE) ? COLOR_WHITE : COLOR_BLACK;
3988             short bg = color;
3989
3990             init_pair(color, fg, bg);
3991             wbkgdset(win, (chtype) (COLOR_PAIR(color) | ' '));
3992         } else {
3993             wbkgdset(win, A_BOLD | ' ');
3994         }
3995     }
3996     return pan;
3997 }                               /* end of mkpanel */
3998
3999 /*+-------------------------------------------------------------------------
4000         rmpanel(pan)
4001 --------------------------------------------------------------------------*/
4002 static void
4003 rmpanel(PANEL * pan)
4004 {
4005     WINDOW *win = panel_window(pan);
4006     del_panel(pan);
4007     delwin(win);
4008 }                               /* end of rmpanel */
4009
4010 /*+-------------------------------------------------------------------------
4011         pflush()
4012 --------------------------------------------------------------------------*/
4013 static void
4014 pflush(void)
4015 {
4016     update_panels();
4017     doupdate();
4018 }                               /* end of pflush */
4019
4020 /*+-------------------------------------------------------------------------
4021         fill_panel(win)
4022 --------------------------------------------------------------------------*/
4023 static void
4024 init_panel(void)
4025 {
4026     register int y, x;
4027
4028     for (y = 0; y < LINES - 1; y++) {
4029         for (x = 0; x < COLS; x++)
4030             wprintw(stdscr, "%d", (y + x) % 10);
4031     }
4032 }
4033
4034 static void
4035 fill_panel(PANEL * pan)
4036 {
4037     WINDOW *win = panel_window(pan);
4038     int num = ((const char *) panel_userptr(pan))[1];
4039     int y, x;
4040
4041     wmove(win, 1, 1);
4042     wprintw(win, "-pan%c-", num);
4043     wclrtoeol(win);
4044     box(win, 0, 0);
4045     for (y = 2; y < getmaxy(win) - 1; y++) {
4046         for (x = 1; x < getmaxx(win) - 1; x++) {
4047             wmove(win, y, x);
4048             waddch(win, UChar(num));
4049         }
4050     }
4051 }
4052
4053 #if USE_WIDEC_SUPPORT
4054 static void
4055 make_fullwidth_digit(cchar_t *target, int digit)
4056 {
4057     wchar_t source[2];
4058
4059     source[0] = digit + 0xff10;
4060     source[1] = 0;
4061     setcchar(target, source, A_NORMAL, 0, 0);
4062 }
4063
4064 static void
4065 init_wide_panel(void)
4066 {
4067     int digit;
4068     cchar_t temp[10];
4069
4070     for (digit = 0; digit < 10; ++digit)
4071         make_fullwidth_digit(&temp[digit], digit);
4072
4073     do {
4074         int y, x;
4075         getyx(stdscr, y, x);
4076         digit = (y + x / 2) % 10;
4077     } while (add_wch(&temp[digit]) != ERR);
4078 }
4079
4080 static void
4081 fill_wide_panel(PANEL * pan)
4082 {
4083     WINDOW *win = panel_window(pan);
4084     int num = ((const char *) panel_userptr(pan))[1];
4085     int y, x;
4086
4087     wmove(win, 1, 1);
4088     wprintw(win, "-pan%c-", num);
4089     wclrtoeol(win);
4090     box(win, 0, 0);
4091     for (y = 2; y < getmaxy(win) - 1; y++) {
4092         for (x = 1; x < getmaxx(win) - 1; x++) {
4093             wmove(win, y, x);
4094             waddch(win, UChar(num));
4095         }
4096     }
4097 }
4098 #endif
4099
4100 #define MAX_PANELS 5
4101
4102 static void
4103 canned_panel(PANEL * px[MAX_PANELS + 1], NCURSES_CONST char *cmd)
4104 {
4105     int which = cmd[1] - '0';
4106
4107     saywhat(cmd);
4108     switch (*cmd) {
4109     case 'h':
4110         hide_panel(px[which]);
4111         break;
4112     case 's':
4113         show_panel(px[which]);
4114         break;
4115     case 't':
4116         top_panel(px[which]);
4117         break;
4118     case 'b':
4119         bottom_panel(px[which]);
4120         break;
4121     case 'd':
4122         rmpanel(px[which]);
4123         break;
4124     }
4125     pflush();
4126     wait_a_while(nap_msec);
4127 }
4128
4129 static void
4130 demo_panels(void (*InitPanel) (void), void (*FillPanel) (PANEL *))
4131 {
4132     int count;
4133     int itmp;
4134     PANEL *px[MAX_PANELS + 1];
4135
4136     scrollok(stdscr, FALSE);    /* we don't want stdscr to scroll! */
4137     refresh();
4138
4139     InitPanel();
4140     for (count = 0; count < 5; count++) {
4141         px[1] = mkpanel(COLOR_RED,
4142                         LINES / 2 - 2,
4143                         COLS / 8 + 1,
4144                         0,
4145                         0);
4146         set_panel_userptr(px[1], (NCURSES_CONST void *) "p1");
4147
4148         px[2] = mkpanel(COLOR_GREEN,
4149                         LINES / 2 + 1,
4150                         COLS / 7,
4151                         LINES / 4,
4152                         COLS / 10);
4153         set_panel_userptr(px[2], (NCURSES_CONST void *) "p2");
4154
4155         px[3] = mkpanel(COLOR_YELLOW,
4156                         LINES / 4,
4157                         COLS / 10,
4158                         LINES / 2,
4159                         COLS / 9);
4160         set_panel_userptr(px[3], (NCURSES_CONST void *) "p3");
4161
4162         px[4] = mkpanel(COLOR_BLUE,
4163                         LINES / 2 - 2,
4164                         COLS / 8,
4165                         LINES / 2 - 2,
4166                         COLS / 3);
4167         set_panel_userptr(px[4], (NCURSES_CONST void *) "p4");
4168
4169         px[5] = mkpanel(COLOR_MAGENTA,
4170                         LINES / 2 - 2,
4171                         COLS / 8,
4172                         LINES / 2,
4173                         COLS / 2 - 2);
4174         set_panel_userptr(px[5], (NCURSES_CONST void *) "p5");
4175
4176         FillPanel(px[1]);
4177         FillPanel(px[2]);
4178         FillPanel(px[3]);
4179         FillPanel(px[4]);
4180         FillPanel(px[5]);
4181
4182         hide_panel(px[4]);
4183         hide_panel(px[5]);
4184         pflush();
4185         saywhat("");
4186         wait_a_while(nap_msec);
4187
4188         saywhat("h3 s1 s2 s4 s5");
4189         move_panel(px[1], 0, 0);
4190         hide_panel(px[3]);
4191         show_panel(px[1]);
4192         show_panel(px[2]);
4193         show_panel(px[4]);
4194         show_panel(px[5]);
4195         pflush();
4196         wait_a_while(nap_msec);
4197
4198         canned_panel(px, "s1");
4199         canned_panel(px, "s2");
4200
4201         saywhat("m2");
4202         move_panel(px[2], LINES / 3 + 1, COLS / 8);
4203         pflush();
4204         wait_a_while(nap_msec);
4205
4206         canned_panel(px, "s3");
4207
4208         saywhat("m3");
4209         move_panel(px[3], LINES / 4 + 1, COLS / 15);
4210         pflush();
4211         wait_a_while(nap_msec);
4212
4213         canned_panel(px, "b3");
4214         canned_panel(px, "s4");
4215         canned_panel(px, "s5");
4216         canned_panel(px, "t3");
4217         canned_panel(px, "t1");
4218         canned_panel(px, "t2");
4219         canned_panel(px, "t3");
4220         canned_panel(px, "t4");
4221
4222         for (itmp = 0; itmp < 6; itmp++) {
4223             WINDOW *w4 = panel_window(px[4]);
4224             WINDOW *w5 = panel_window(px[5]);
4225
4226             saywhat("m4");
4227             wmove(w4, LINES / 8, 1);
4228             waddstr(w4, mod[itmp]);
4229             move_panel(px[4], LINES / 6, itmp * (COLS / 8));
4230             wmove(w5, LINES / 6, 1);
4231             waddstr(w5, mod[itmp]);
4232             pflush();
4233             wait_a_while(nap_msec);
4234
4235             saywhat("m5");
4236             wmove(w4, LINES / 6, 1);
4237             waddstr(w4, mod[itmp]);
4238             move_panel(px[5], LINES / 3 - 1, (itmp * 10) + 6);
4239             wmove(w5, LINES / 8, 1);
4240             waddstr(w5, mod[itmp]);
4241             pflush();
4242             wait_a_while(nap_msec);
4243         }
4244
4245         saywhat("m4");
4246         move_panel(px[4], LINES / 6, itmp * (COLS / 8));
4247         pflush();
4248         wait_a_while(nap_msec);
4249
4250         canned_panel(px, "t5");
4251         canned_panel(px, "t2");
4252         canned_panel(px, "t1");
4253         canned_panel(px, "d2");
4254         canned_panel(px, "h3");
4255         canned_panel(px, "d1");
4256         canned_panel(px, "d4");
4257         canned_panel(px, "d5");
4258         canned_panel(px, "d3");
4259
4260         wait_a_while(nap_msec);
4261         if (nap_msec == 1)
4262             break;
4263         nap_msec = 100L;
4264     }
4265
4266     erase();
4267     endwin();
4268 }
4269 #endif /* USE_LIBPANEL */
4270
4271 /****************************************************************************
4272  *
4273  * Pad tester
4274  *
4275  ****************************************************************************/
4276
4277 #define GRIDSIZE        3
4278
4279 static bool pending_pan = FALSE;
4280 static bool show_panner_legend = TRUE;
4281
4282 static int
4283 panner_legend(int line)
4284 {
4285     static const char *const legend[] =
4286     {
4287         "Use arrow keys (or U,D,L,R) to pan, ESC to quit, ! to shell-out.",
4288         "Use +,- (or j,k) to grow/shrink the panner vertically.",
4289         "Use <,> (or h,l) to grow/shrink the panner horizontally.",
4290         "Number repeats.  Toggle legend:? filler:a timer:t scrollmark:s."
4291     };
4292     int n = (SIZEOF(legend) - (LINES - line));
4293     if (line < LINES && (n >= 0)) {
4294         move(line, 0);
4295         if (show_panner_legend)
4296             printw("%s", legend[n]);
4297         clrtoeol();
4298         return show_panner_legend;
4299     }
4300     return FALSE;
4301 }
4302
4303 static void
4304 panner_h_cleanup(int from_y, int from_x, int to_x)
4305 {
4306     if (!panner_legend(from_y))
4307         do_h_line(from_y, from_x, ' ', to_x);
4308 }
4309
4310 static void
4311 panner_v_cleanup(int from_y, int from_x, int to_y)
4312 {
4313     if (!panner_legend(from_y))
4314         do_v_line(from_y, from_x, ' ', to_y);
4315 }
4316
4317 static void
4318 fill_pad(WINDOW *panpad, bool pan_lines)
4319 {
4320     int y, x;
4321     unsigned gridcount = 0;
4322
4323     wmove(panpad, 0, 0);
4324     for (y = 0; y < getmaxy(panpad); y++) {
4325         for (x = 0; x < getmaxx(panpad); x++) {
4326             if (y % GRIDSIZE == 0 && x % GRIDSIZE == 0) {
4327                 if (y == 0 && x == 0)
4328                     waddch(panpad, pan_lines ? ACS_ULCORNER : '+');
4329                 else if (y == 0)
4330                     waddch(panpad, pan_lines ? ACS_TTEE : '+');
4331                 else if (y == 0 || x == 0)
4332                     waddch(panpad, pan_lines ? ACS_LTEE : '+');
4333                 else
4334                     waddch(panpad, (chtype) ((pan_lines ? 'a' : 'A') +
4335                                              (gridcount++ % 26)));
4336             } else if (y % GRIDSIZE == 0)
4337                 waddch(panpad, pan_lines ? ACS_HLINE : '-');
4338             else if (x % GRIDSIZE == 0)
4339                 waddch(panpad, pan_lines ? ACS_VLINE : '|');
4340             else
4341                 waddch(panpad, ' ');
4342         }
4343     }
4344 }
4345
4346 static void
4347 panner(WINDOW *pad,
4348        int top_x, int top_y, int porty, int portx,
4349        int (*pgetc) (WINDOW *))
4350 {
4351 #if HAVE_GETTIMEOFDAY
4352     struct timeval before, after;
4353     bool timing = TRUE;
4354 #endif
4355     bool pan_lines = FALSE;
4356     bool scrollers = TRUE;
4357     int basex = 0;
4358     int basey = 0;
4359     int pxmax, pymax, lowend, highend, c;
4360
4361     getmaxyx(pad, pymax, pxmax);
4362     scrollok(stdscr, FALSE);    /* we don't want stdscr to scroll! */
4363
4364     c = KEY_REFRESH;
4365     do {
4366 #ifdef NCURSES_VERSION
4367         /*
4368          * During shell-out, the user may have resized the window.  Adjust
4369          * the port size of the pad to accommodate this.  Ncurses automatically
4370          * resizes all of the normal windows to fit on the new screen.
4371          */
4372         if (top_x > COLS)
4373             top_x = COLS;
4374         if (portx > COLS)
4375             portx = COLS;
4376         if (top_y > LINES)
4377             top_y = LINES;
4378         if (porty > LINES)
4379             porty = LINES;
4380 #endif
4381         switch (c) {
4382         case KEY_REFRESH:
4383             erase();
4384
4385             /* FALLTHRU */
4386         case '?':
4387             if (c == '?')
4388                 show_panner_legend = !show_panner_legend;
4389             panner_legend(LINES - 4);
4390             panner_legend(LINES - 3);
4391             panner_legend(LINES - 2);
4392             panner_legend(LINES - 1);
4393             break;
4394         case 'a':
4395             pan_lines = !pan_lines;
4396             fill_pad(pad, pan_lines);
4397             pending_pan = FALSE;
4398             break;
4399
4400 #if HAVE_GETTIMEOFDAY
4401         case 't':
4402             timing = !timing;
4403             if (!timing)
4404                 panner_legend(LINES - 1);
4405             break;
4406 #endif
4407         case 's':
4408             scrollers = !scrollers;
4409             break;
4410
4411             /* Move the top-left corner of the pad, keeping the bottom-right
4412              * corner fixed.
4413              */
4414         case 'h':               /* increase-columns: move left edge to left */
4415             if (top_x <= 0)
4416                 beep();
4417             else {
4418                 panner_v_cleanup(top_y, top_x, porty);
4419                 top_x--;
4420             }
4421             break;
4422
4423         case 'j':               /* decrease-lines: move top-edge down */
4424             if (top_y >= porty)
4425                 beep();
4426             else {
4427                 panner_h_cleanup(top_y - 1, top_x - (top_x > 0), portx);
4428                 top_y++;
4429             }
4430             break;
4431
4432         case 'k':               /* increase-lines: move top-edge up */
4433             if (top_y <= 0)
4434                 beep();
4435             else {
4436                 top_y--;
4437                 panner_h_cleanup(top_y, top_x, portx);
4438             }
4439             break;
4440
4441         case 'l':               /* decrease-columns: move left-edge to right */
4442             if (top_x >= portx)
4443                 beep();
4444             else {
4445                 panner_v_cleanup(top_y - (top_y > 0), top_x - 1, porty);
4446                 top_x++;
4447             }
4448             break;
4449
4450             /* Move the bottom-right corner of the pad, keeping the top-left
4451              * corner fixed.
4452              */
4453         case KEY_IC:            /* increase-columns: move right-edge to right */
4454             if (portx >= pxmax || portx >= COLS)
4455                 beep();
4456             else {
4457                 panner_v_cleanup(top_y - (top_y > 0), portx - 1, porty);
4458                 ++portx;
4459             }
4460             break;
4461
4462         case KEY_IL:            /* increase-lines: move bottom-edge down */
4463             if (porty >= pymax || porty >= LINES)
4464                 beep();
4465             else {
4466                 panner_h_cleanup(porty - 1, top_x - (top_x > 0), portx);
4467                 ++porty;
4468             }
4469             break;
4470
4471         case KEY_DC:            /* decrease-columns: move bottom edge up */
4472             if (portx <= top_x)
4473                 beep();
4474             else {
4475                 portx--;
4476                 panner_v_cleanup(top_y - (top_y > 0), portx, porty);
4477             }
4478             break;
4479
4480         case KEY_DL:            /* decrease-lines */
4481             if (porty <= top_y)
4482                 beep();
4483             else {
4484                 porty--;
4485                 panner_h_cleanup(porty, top_x - (top_x > 0), portx);
4486             }
4487             break;
4488
4489         case KEY_LEFT:          /* pan leftwards */
4490             if (basex > 0)
4491                 basex--;
4492             else
4493                 beep();
4494             break;
4495
4496         case KEY_RIGHT: /* pan rightwards */
4497             if (basex + portx - (pymax > porty) < pxmax)
4498                 basex++;
4499             else
4500                 beep();
4501             break;
4502
4503         case KEY_UP:            /* pan upwards */
4504             if (basey > 0)
4505                 basey--;
4506             else
4507                 beep();
4508             break;
4509
4510         case KEY_DOWN:          /* pan downwards */
4511             if (basey + porty - (pxmax > portx) < pymax)
4512                 basey++;
4513             else
4514                 beep();
4515             break;
4516
4517         case 'H':
4518         case KEY_HOME:
4519         case KEY_FIND:
4520             basey = 0;
4521             break;
4522
4523         case 'E':
4524         case KEY_END:
4525         case KEY_SELECT:
4526             basey = pymax - porty;
4527             if (basey < 0)
4528                 basey = 0;
4529             break;
4530
4531         default:
4532             beep();
4533             break;
4534         }
4535
4536         mvaddch(top_y - 1, top_x - 1, ACS_ULCORNER);
4537         do_v_line(top_y, top_x - 1, ACS_VLINE, porty);
4538         do_h_line(top_y - 1, top_x, ACS_HLINE, portx);
4539
4540         if (scrollers && (pxmax > portx - 1)) {
4541             int length = (portx - top_x - 1);
4542             float ratio = ((float) length) / ((float) pxmax);
4543
4544             lowend = (int) (top_x + (basex * ratio));
4545             highend = (int) (top_x + ((basex + length) * ratio));
4546
4547             do_h_line(porty - 1, top_x, ACS_HLINE, lowend);
4548             if (highend < portx) {
4549                 attron(A_REVERSE);
4550                 do_h_line(porty - 1, lowend, ' ', highend + 1);
4551                 attroff(A_REVERSE);
4552                 do_h_line(porty - 1, highend + 1, ACS_HLINE, portx);
4553             }
4554         } else
4555             do_h_line(porty - 1, top_x, ACS_HLINE, portx);
4556
4557         if (scrollers && (pymax > porty - 1)) {
4558             int length = (porty - top_y - 1);
4559             float ratio = ((float) length) / ((float) pymax);
4560
4561             lowend = (int) (top_y + (basey * ratio));
4562             highend = (int) (top_y + ((basey + length) * ratio));
4563
4564             do_v_line(top_y, portx - 1, ACS_VLINE, lowend);
4565             if (highend < porty) {
4566                 attron(A_REVERSE);
4567                 do_v_line(lowend, portx - 1, ' ', highend + 1);
4568                 attroff(A_REVERSE);
4569                 do_v_line(highend + 1, portx - 1, ACS_VLINE, porty);
4570             }
4571         } else
4572             do_v_line(top_y, portx - 1, ACS_VLINE, porty);
4573
4574         mvaddch(top_y - 1, portx - 1, ACS_URCORNER);
4575         mvaddch(porty - 1, top_x - 1, ACS_LLCORNER);
4576         mvaddch(porty - 1, portx - 1, ACS_LRCORNER);
4577
4578         if (!pending_pan) {
4579 #if HAVE_GETTIMEOFDAY
4580             gettimeofday(&before, 0);
4581 #endif
4582             wnoutrefresh(stdscr);
4583
4584             pnoutrefresh(pad,
4585                          basey, basex,
4586                          top_y, top_x,
4587                          porty - (pxmax > portx) - 1,
4588                          portx - (pymax > porty) - 1);
4589
4590             doupdate();
4591 #if HAVE_GETTIMEOFDAY
4592             if (timing) {
4593                 double elapsed;
4594                 gettimeofday(&after, 0);
4595                 elapsed = (after.tv_sec + after.tv_usec / 1.0e6)
4596                     - (before.tv_sec + before.tv_usec / 1.0e6);
4597                 move(LINES - 1, COLS - 12);
4598                 printw("Secs: %2.03f", elapsed);
4599                 refresh();
4600             }
4601 #endif
4602         }
4603
4604     } while
4605         ((c = pgetc(pad)) != KEY_EXIT);
4606
4607     scrollok(stdscr, TRUE);     /* reset to driver's default */
4608 }
4609
4610 static int
4611 padgetch(WINDOW *win)
4612 {
4613     static int count;
4614     static int last;
4615     int c;
4616
4617     if ((pending_pan = (count > 0)) != FALSE) {
4618         count--;
4619         pending_pan = (count != 0);
4620     } else {
4621         for (;;) {
4622             switch (c = wGetchar(win)) {
4623             case '!':
4624                 ShellOut(FALSE);
4625                 /* FALLTHRU */
4626             case CTRL('r'):
4627                 endwin();
4628                 refresh();
4629                 c = KEY_REFRESH;
4630                 break;
4631             case CTRL('l'):
4632                 c = KEY_REFRESH;
4633                 break;
4634             case 'U':
4635                 c = KEY_UP;
4636                 break;
4637             case 'D':
4638                 c = KEY_DOWN;
4639                 break;
4640             case 'R':
4641                 c = KEY_RIGHT;
4642                 break;
4643             case 'L':
4644                 c = KEY_LEFT;
4645                 break;
4646             case '+':
4647                 c = KEY_IL;
4648                 break;
4649             case '-':
4650                 c = KEY_DL;
4651                 break;
4652             case '>':
4653                 c = KEY_IC;
4654                 break;
4655             case '<':
4656                 c = KEY_DC;
4657                 break;
4658             case ERR:           /* FALLTHRU */
4659               case_QUIT:
4660                 count = 0;
4661                 c = KEY_EXIT;
4662                 break;
4663             default:
4664                 if (c >= '0' && c <= '9') {
4665                     count = count * 10 + (c - '0');
4666                     continue;
4667                 }
4668                 break;
4669             }
4670             last = c;
4671             break;
4672         }
4673         if (count > 0)
4674             count--;
4675     }
4676     return (last);
4677 }
4678
4679 #define PAD_HIGH 200
4680 #define PAD_WIDE 200
4681
4682 static void
4683 demo_pad(void)
4684 /* Demonstrate pads. */
4685 {
4686     WINDOW *panpad = newpad(PAD_HIGH, PAD_WIDE);
4687
4688     if (panpad == 0) {
4689         Cannot("cannot create requested pad");
4690         return;
4691     }
4692
4693     fill_pad(panpad, FALSE);
4694
4695     panner_legend(LINES - 4);
4696     panner_legend(LINES - 3);
4697     panner_legend(LINES - 2);
4698     panner_legend(LINES - 1);
4699
4700     keypad(panpad, TRUE);
4701
4702     /* Make the pad (initially) narrow enough that a trace file won't wrap.
4703      * We'll still be able to widen it during a test, since that's required
4704      * for testing boundaries.
4705      */
4706     panner(panpad, 2, 2, LINES - 5, COLS - 15, padgetch);
4707
4708     delwin(panpad);
4709     endwin();
4710     erase();
4711 }
4712
4713 /****************************************************************************
4714  *
4715  * Tests from John Burnell's PDCurses tester
4716  *
4717  ****************************************************************************/
4718
4719 static void
4720 Continue(WINDOW *win)
4721 {
4722     noecho();
4723     wmove(win, 10, 1);
4724     mvwaddstr(win, 10, 1, " Press any key to continue");
4725     wrefresh(win);
4726     wGetchar(win);
4727 }
4728
4729 static void
4730 flushinp_test(WINDOW *win)
4731 /* Input test, adapted from John Burnell's PDCurses tester */
4732 {
4733     int w, h, bx, by, sw, sh, i;
4734
4735     WINDOW *subWin;
4736     wclear(win);
4737
4738     getmaxyx(win, h, w);
4739     getbegyx(win, by, bx);
4740     sw = w / 3;
4741     sh = h / 3;
4742     if ((subWin = subwin(win, sh, sw, by + h - sh - 2, bx + w - sw - 2)) == 0)
4743         return;
4744
4745 #ifdef A_COLOR
4746     if (use_colors) {
4747         init_pair(2, COLOR_CYAN, COLOR_BLUE);
4748         wbkgd(subWin, COLOR_PAIR(2) | ' ');
4749     }
4750 #endif
4751     wattrset(subWin, A_BOLD);
4752     box(subWin, ACS_VLINE, ACS_HLINE);
4753     mvwaddstr(subWin, 2, 1, "This is a subwindow");
4754     wrefresh(win);
4755
4756     /*
4757      * This used to set 'nocbreak()'.  However, Alexander Lukyanov says that
4758      * it only happened to "work" on SVr4 because that implementation does not
4759      * emulate nocbreak+noecho mode, whereas ncurses does.  To get the desired
4760      * test behavior, we're using 'cbreak()', which will allow a single
4761      * character to return without needing a newline. - T.Dickey 1997/10/11.
4762      */
4763     cbreak();
4764     mvwaddstr(win, 0, 1, "This is a test of the flushinp() call.");
4765
4766     mvwaddstr(win, 2, 1, "Type random keys for 5 seconds.");
4767     mvwaddstr(win, 3, 1,
4768               "These should be discarded (not echoed) after the subwindow goes away.");
4769     wrefresh(win);
4770
4771     for (i = 0; i < 5; i++) {
4772         mvwprintw(subWin, 1, 1, "Time = %d", i);
4773         wrefresh(subWin);
4774         napms(1000);
4775         flushinp();
4776     }
4777
4778     delwin(subWin);
4779     werase(win);
4780     flash();
4781     wrefresh(win);
4782     napms(1000);
4783
4784     mvwaddstr(win, 2, 1,
4785               "If you were still typing when the window timer expired,");
4786     mvwaddstr(win, 3, 1,
4787               "or else you typed nothing at all while it was running,");
4788     mvwaddstr(win, 4, 1,
4789               "test was invalid.  You'll see garbage or nothing at all. ");
4790     mvwaddstr(win, 6, 1, "Press a key");
4791     wmove(win, 9, 10);
4792     wrefresh(win);
4793     echo();
4794     wGetchar(win);
4795     flushinp();
4796     mvwaddstr(win, 12, 0,
4797               "If you see any key other than what you typed, flushinp() is broken.");
4798     Continue(win);
4799
4800     wmove(win, 9, 10);
4801     wdelch(win);
4802     wrefresh(win);
4803     wmove(win, 12, 0);
4804     clrtoeol();
4805     waddstr(win,
4806             "What you typed should now have been deleted; if not, wdelch() failed.");
4807     Continue(win);
4808
4809     cbreak();
4810 }
4811
4812 /****************************************************************************
4813  *
4814  * Menu test
4815  *
4816  ****************************************************************************/
4817
4818 #if USE_LIBMENU
4819
4820 #define MENU_Y  8
4821 #define MENU_X  8
4822
4823 static int
4824 menu_virtualize(int c)
4825 {
4826     if (c == '\n' || c == KEY_EXIT)
4827         return (MAX_COMMAND + 1);
4828     else if (c == 'u')
4829         return (REQ_SCR_ULINE);
4830     else if (c == 'd')
4831         return (REQ_SCR_DLINE);
4832     else if (c == 'b' || c == KEY_NPAGE)
4833         return (REQ_SCR_UPAGE);
4834     else if (c == 'f' || c == KEY_PPAGE)
4835         return (REQ_SCR_DPAGE);
4836     else if (c == 'n' || c == KEY_DOWN)
4837         return (REQ_NEXT_ITEM);
4838     else if (c == 'p' || c == KEY_UP)
4839         return (REQ_PREV_ITEM);
4840     else if (c == ' ')
4841         return (REQ_TOGGLE_ITEM);
4842     else {
4843         if (c != KEY_MOUSE)
4844             beep();
4845         return (c);
4846     }
4847 }
4848
4849 static const char *animals[] =
4850 {
4851     "Lions",
4852     "Tigers",
4853     "Bears",
4854     "(Oh my!)",
4855     "Newts",
4856     "Platypi",
4857     "Lemurs",
4858     "(Oh really?!)",
4859     "Leopards",
4860     "Panthers",
4861     "Pumas",
4862     "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
4863     "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs, Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
4864     (char *) 0
4865 };
4866
4867 static void
4868 menu_test(void)
4869 {
4870     MENU *m;
4871     ITEM *items[SIZEOF(animals)];
4872     ITEM **ip = items;
4873     const char **ap;
4874     int mrows, mcols, c;
4875     WINDOW *menuwin;
4876
4877 #ifdef NCURSES_MOUSE_VERSION
4878     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
4879 #endif
4880     mvaddstr(0, 0, "This is the menu test:");
4881     mvaddstr(2, 0, "  Use up and down arrow to move the select bar.");
4882     mvaddstr(3, 0, "  'n' and 'p' act like arrows.");
4883     mvaddstr(4, 0,
4884              "  'b' and 'f' scroll up/down (page), 'u' and 'd' (line).");
4885     mvaddstr(5, 0, "  Press return to exit.");
4886     refresh();
4887
4888     for (ap = animals; *ap; ap++)
4889         *ip++ = new_item(*ap, "");
4890     *ip = (ITEM *) 0;
4891
4892     m = new_menu(items);
4893
4894     set_menu_format(m, (SIZEOF(animals) + 1) / 2, 1);
4895     scale_menu(m, &mrows, &mcols);
4896
4897     menuwin = newwin(mrows + 2, mcols + 2, MENU_Y, MENU_X);
4898     set_menu_win(m, menuwin);
4899     keypad(menuwin, TRUE);
4900     box(menuwin, 0, 0);
4901
4902     set_menu_sub(m, derwin(menuwin, mrows, mcols, 1, 1));
4903
4904     post_menu(m);
4905
4906     while ((c = menu_driver(m, menu_virtualize(wGetchar(menuwin)))) != E_UNKNOWN_COMMAND) {
4907         if (c == E_NOT_POSTED)
4908             break;
4909         if (c == E_REQUEST_DENIED)
4910             beep();
4911         continue;
4912     }
4913
4914     (void) mvprintw(LINES - 2, 0,
4915                     "You chose: %s\n", item_name(current_item(m)));
4916     (void) addstr("Press any key to continue...");
4917     wGetchar(stdscr);
4918
4919     unpost_menu(m);
4920     delwin(menuwin);
4921
4922     free_menu(m);
4923     for (ip = items; *ip; ip++)
4924         free_item(*ip);
4925 #ifdef NCURSES_MOUSE_VERSION
4926     mousemask(0, (mmask_t *) 0);
4927 #endif
4928 }
4929
4930 #ifdef TRACE
4931 #define T_TBL(name) { #name, name }
4932 static struct {
4933     const char *name;
4934     unsigned mask;
4935 } t_tbl[] = {
4936
4937     T_TBL(TRACE_DISABLE),
4938         T_TBL(TRACE_TIMES),
4939         T_TBL(TRACE_TPUTS),
4940         T_TBL(TRACE_UPDATE),
4941         T_TBL(TRACE_MOVE),
4942         T_TBL(TRACE_CHARPUT),
4943         T_TBL(TRACE_ORDINARY),
4944         T_TBL(TRACE_CALLS),
4945         T_TBL(TRACE_VIRTPUT),
4946         T_TBL(TRACE_IEVENT),
4947         T_TBL(TRACE_BITS),
4948         T_TBL(TRACE_ICALLS),
4949         T_TBL(TRACE_CCALLS),
4950         T_TBL(TRACE_DATABASE),
4951         T_TBL(TRACE_ATTRS),
4952         T_TBL(TRACE_MAXIMUM),
4953     {
4954         (char *) 0, 0
4955     }
4956 };
4957
4958 static char *
4959 tracetrace(unsigned tlevel)
4960 {
4961     static char *buf;
4962     int n;
4963
4964     if (buf == 0) {
4965         size_t need = 12;
4966         for (n = 0; t_tbl[n].name != 0; n++)
4967             need += strlen(t_tbl[n].name) + 2;
4968         buf = (char *) malloc(need);
4969     }
4970     sprintf(buf, "0x%02x = {", tlevel);
4971     if (tlevel == 0) {
4972         sprintf(buf + strlen(buf), "%s, ", t_tbl[0].name);
4973     } else {
4974         for (n = 1; t_tbl[n].name != 0; n++)
4975             if ((tlevel & t_tbl[n].mask) == t_tbl[n].mask) {
4976                 strcat(buf, t_tbl[n].name);
4977                 strcat(buf, ", ");
4978             }
4979     }
4980     if (buf[strlen(buf) - 2] == ',')
4981         buf[strlen(buf) - 2] = '\0';
4982     return (strcat(buf, "}"));
4983 }
4984
4985 /* fake a dynamically reconfigurable menu using the 0th entry to deselect
4986  * the others
4987  */
4988 static int
4989 run_trace_menu(MENU * m)
4990 {
4991     ITEM **items;
4992     ITEM *i, **p;
4993
4994     for (;;) {
4995         bool changed = FALSE;
4996         switch (menu_driver(m, menu_virtualize(wGetchar(menu_win(m))))) {
4997         case E_UNKNOWN_COMMAND:
4998             return FALSE;
4999         default:
5000             items = menu_items(m);
5001             i = current_item(m);
5002             if (i == items[0]) {
5003                 if (item_value(i)) {
5004                     for (p = items + 1; *p != 0; p++)
5005                         if (item_value(*p)) {
5006                             set_item_value(*p, FALSE);
5007                             changed = TRUE;
5008                         }
5009                 }
5010             } else {
5011                 for (p = items + 1; *p != 0; p++)
5012                     if (item_value(*p)) {
5013                         set_item_value(items[0], FALSE);
5014                         changed = TRUE;
5015                         break;
5016                     }
5017             }
5018             if (!changed)
5019                 return TRUE;
5020         }
5021     }
5022 }
5023
5024 static void
5025 trace_set(void)
5026 /* interactively set the trace level */
5027 {
5028     MENU *m;
5029     ITEM *items[SIZEOF(t_tbl)];
5030     ITEM **ip = items;
5031     int mrows, mcols;
5032     unsigned newtrace;
5033     int n;
5034     WINDOW *menuwin;
5035
5036     mvaddstr(0, 0, "Interactively set trace level:");
5037     mvaddstr(2, 0, "  Press space bar to toggle a selection.");
5038     mvaddstr(3, 0, "  Use up and down arrow to move the select bar.");
5039     mvaddstr(4, 0, "  Press return to set the trace level.");
5040     mvprintw(6, 0, "(Current trace level is %s)", tracetrace(_nc_tracing));
5041
5042     refresh();
5043
5044     for (n = 0; t_tbl[n].name != 0; n++)
5045         *ip++ = new_item(t_tbl[n].name, "");
5046     *ip = (ITEM *) 0;
5047
5048     m = new_menu(items);
5049
5050     set_menu_format(m, 0, 2);
5051     scale_menu(m, &mrows, &mcols);
5052
5053     menu_opts_off(m, O_ONEVALUE);
5054     menuwin = newwin(mrows + 2, mcols + 2, MENU_Y, MENU_X);
5055     set_menu_win(m, menuwin);
5056     keypad(menuwin, TRUE);
5057     box(menuwin, 0, 0);
5058
5059     set_menu_sub(m, derwin(menuwin, mrows, mcols, 1, 1));
5060
5061     post_menu(m);
5062
5063     for (ip = menu_items(m); *ip; ip++) {
5064         unsigned mask = t_tbl[item_index(*ip)].mask;
5065         if (mask == 0)
5066             set_item_value(*ip, _nc_tracing == 0);
5067         else if ((mask & _nc_tracing) == mask)
5068             set_item_value(*ip, TRUE);
5069     }
5070
5071     while (run_trace_menu(m))
5072         continue;
5073
5074     newtrace = 0;
5075     for (ip = menu_items(m); *ip; ip++)
5076         if (item_value(*ip))
5077             newtrace |= t_tbl[item_index(*ip)].mask;
5078     trace(newtrace);
5079     _tracef("trace level interactively set to %s", tracetrace(_nc_tracing));
5080
5081     (void) mvprintw(LINES - 2, 0,
5082                     "Trace level is %s\n", tracetrace(_nc_tracing));
5083     (void) addstr("Press any key to continue...");
5084     wGetchar(stdscr);
5085
5086     unpost_menu(m);
5087     delwin(menuwin);
5088
5089     free_menu(m);
5090     for (ip = items; *ip; ip++)
5091         free_item(*ip);
5092 }
5093 #endif /* TRACE */
5094 #endif /* USE_LIBMENU */
5095
5096 /****************************************************************************
5097  *
5098  * Forms test
5099  *
5100  ****************************************************************************/
5101 #if USE_LIBFORM
5102 static FIELD *
5103 make_label(int frow, int fcol, NCURSES_CONST char *label)
5104 {
5105     FIELD *f = new_field(1, (int) strlen(label), frow, fcol, 0, 0);
5106
5107     if (f) {
5108         set_field_buffer(f, 0, label);
5109         set_field_opts(f, (int) (field_opts(f) & ~O_ACTIVE));
5110     }
5111     return (f);
5112 }
5113
5114 static FIELD *
5115 make_field(int frow, int fcol, int rows, int cols, bool secure)
5116 {
5117     FIELD *f = new_field(rows, cols, frow, fcol, 0, secure ? 1 : 0);
5118
5119     if (f) {
5120         set_field_back(f, A_UNDERLINE);
5121         set_field_userptr(f, (void *) 0);
5122     }
5123     return (f);
5124 }
5125
5126 static void
5127 display_form(FORM * f)
5128 {
5129     WINDOW *w;
5130     int rows, cols;
5131
5132     scale_form(f, &rows, &cols);
5133
5134     if ((w = newwin(rows + 2, cols + 4, 0, 0)) != (WINDOW *) 0) {
5135         set_form_win(f, w);
5136         set_form_sub(f, derwin(w, rows, cols, 1, 2));
5137         box(w, 0, 0);
5138         keypad(w, TRUE);
5139     }
5140
5141     if (post_form(f) != E_OK)
5142         wrefresh(w);
5143 }
5144
5145 static void
5146 erase_form(FORM * f)
5147 {
5148     WINDOW *w = form_win(f);
5149     WINDOW *s = form_sub(f);
5150
5151     unpost_form(f);
5152     werase(w);
5153     wrefresh(w);
5154     delwin(s);
5155     delwin(w);
5156 }
5157
5158 static int
5159 edit_secure(FIELD * me, int c)
5160 {
5161     int rows, cols, frow, fcol, nrow, nbuf;
5162
5163     if (field_info(me, &rows, &cols, &frow, &fcol, &nrow, &nbuf) == E_OK
5164         && nbuf > 0) {
5165         char *source = field_buffer(me, 1);
5166         char temp[80];
5167         long len;
5168
5169         strcpy(temp, source ? source : "");
5170         len = (long) (char *) field_userptr(me);
5171         if (c <= KEY_MAX) {
5172             if (isgraph(c) && (len + 1) < (int) sizeof(temp)) {
5173                 temp[len++] = c;
5174                 temp[len] = 0;
5175                 set_field_buffer(me, 1, temp);
5176                 c = '*';
5177             } else {
5178                 c = 0;
5179             }
5180         } else {
5181             switch (c) {
5182             case REQ_BEG_FIELD:
5183             case REQ_CLR_EOF:
5184             case REQ_CLR_EOL:
5185             case REQ_DEL_LINE:
5186             case REQ_DEL_WORD:
5187             case REQ_DOWN_CHAR:
5188             case REQ_END_FIELD:
5189             case REQ_INS_CHAR:
5190             case REQ_INS_LINE:
5191             case REQ_LEFT_CHAR:
5192             case REQ_NEW_LINE:
5193             case REQ_NEXT_WORD:
5194             case REQ_PREV_WORD:
5195             case REQ_RIGHT_CHAR:
5196             case REQ_UP_CHAR:
5197                 c = 0;          /* we don't want to do inline editing */
5198                 break;
5199             case REQ_CLR_FIELD:
5200                 if (len) {
5201                     temp[0] = 0;
5202                     set_field_buffer(me, 1, temp);
5203                 }
5204                 break;
5205             case REQ_DEL_CHAR:
5206             case REQ_DEL_PREV:
5207                 if (len) {
5208                     temp[--len] = 0;
5209                     set_field_buffer(me, 1, temp);
5210                 }
5211                 break;
5212             }
5213         }
5214         set_field_userptr(me, (void *) len);
5215     }
5216     return c;
5217 }
5218
5219 static int
5220 form_virtualize(FORM * f, WINDOW *w)
5221 {
5222     /* *INDENT-OFF* */
5223     static const struct {
5224         int code;
5225         int result;
5226     } lookup[] = {
5227         { CTRL('A'),    REQ_NEXT_CHOICE },
5228         { CTRL('B'),    REQ_PREV_WORD },
5229         { CTRL('C'),    REQ_CLR_EOL },
5230         { CTRL('D'),    REQ_DOWN_FIELD },
5231         { CTRL('E'),    REQ_END_FIELD },
5232         { CTRL('F'),    REQ_NEXT_PAGE },
5233         { CTRL('G'),    REQ_DEL_WORD },
5234         { CTRL('H'),    REQ_DEL_PREV },
5235         { CTRL('I'),    REQ_INS_CHAR },
5236         { CTRL('K'),    REQ_CLR_EOF },
5237         { CTRL('L'),    REQ_LEFT_FIELD },
5238         { CTRL('M'),    REQ_NEW_LINE },
5239         { CTRL('N'),    REQ_NEXT_FIELD },
5240         { CTRL('O'),    REQ_INS_LINE },
5241         { CTRL('P'),    REQ_PREV_FIELD },
5242         { CTRL('R'),    REQ_RIGHT_FIELD },
5243         { CTRL('S'),    REQ_BEG_FIELD },
5244         { CTRL('U'),    REQ_UP_FIELD },
5245         { CTRL('V'),    REQ_DEL_CHAR },
5246         { CTRL('W'),    REQ_NEXT_WORD },
5247         { CTRL('X'),    REQ_CLR_FIELD },
5248         { CTRL('Y'),    REQ_DEL_LINE },
5249         { CTRL('Z'),    REQ_PREV_CHOICE },
5250         { ESCAPE,       MAX_FORM_COMMAND + 1 },
5251         { KEY_BACKSPACE, REQ_DEL_PREV },
5252         { KEY_DOWN,     REQ_DOWN_CHAR },
5253         { KEY_END,      REQ_LAST_FIELD },
5254         { KEY_HOME,     REQ_FIRST_FIELD },
5255         { KEY_LEFT,     REQ_LEFT_CHAR },
5256         { KEY_LL,       REQ_LAST_FIELD },
5257         { KEY_NEXT,     REQ_NEXT_FIELD },
5258         { KEY_NPAGE,    REQ_NEXT_PAGE },
5259         { KEY_PPAGE,    REQ_PREV_PAGE },
5260         { KEY_PREVIOUS, REQ_PREV_FIELD },
5261         { KEY_RIGHT,    REQ_RIGHT_CHAR },
5262         { KEY_UP,       REQ_UP_CHAR },
5263         { QUIT,         MAX_FORM_COMMAND + 1 }
5264     };
5265     /* *INDENT-ON* */
5266
5267     static int mode = REQ_INS_MODE;
5268     int c = wGetchar(w);
5269     unsigned n;
5270     FIELD *me = current_field(f);
5271     bool current = TRUE;
5272
5273     if (c == CTRL(']')) {
5274         if (mode == REQ_INS_MODE) {
5275             mode = REQ_OVL_MODE;
5276         } else {
5277             mode = REQ_INS_MODE;
5278         }
5279         c = mode;
5280     } else {
5281         for (n = 0; n < SIZEOF(lookup); n++) {
5282             if (lookup[n].code == c) {
5283                 c = lookup[n].result;
5284                 break;
5285             }
5286         }
5287     }
5288     mvprintw(0, COLS - 6, "(%s)", mode == REQ_INS_MODE ? "INS" : "OVL");
5289
5290     /*
5291      * Force the field that the user is typing into to be in reverse video,
5292      * while the other fields are shown underlined.
5293      */
5294     switch (c) {
5295     case REQ_BEG_FIELD:
5296     case REQ_CLR_EOF:
5297     case REQ_CLR_EOL:
5298     case REQ_CLR_FIELD:
5299     case REQ_DEL_CHAR:
5300     case REQ_DEL_LINE:
5301     case REQ_DEL_PREV:
5302     case REQ_DEL_WORD:
5303     case REQ_END_FIELD:
5304     case REQ_INS_CHAR:
5305     case REQ_INS_LINE:
5306     case REQ_LEFT_CHAR:
5307     case REQ_LEFT_FIELD:
5308     case REQ_NEXT_WORD:
5309     case REQ_RIGHT_CHAR:
5310         current = TRUE;
5311         break;
5312     default:
5313         current = (c < KEY_MAX);
5314         break;
5315     }
5316     if (current) {
5317         c = edit_secure(me, c);
5318         set_field_back(me, A_REVERSE);
5319     } else {
5320         c = edit_secure(me, c);
5321         set_field_back(me, A_UNDERLINE);
5322     }
5323     return c;
5324 }
5325
5326 static int
5327 my_form_driver(FORM * form, int c)
5328 {
5329     if (c == (MAX_FORM_COMMAND + 1)
5330         && form_driver(form, REQ_VALIDATION) == E_OK)
5331         return (TRUE);
5332     else {
5333         beep();
5334         return (FALSE);
5335     }
5336 }
5337
5338 #ifdef NCURSES_VERSION
5339 #define FIELDCHECK_CB(func) bool func(FIELD * fld, const void * data GCC_UNUSED)
5340 #define CHAR_CHECK_CB(func) bool func(int ch, const void *data GCC_UNUSED)
5341 #else
5342 #define FIELDCHECK_CB(func) int func(FIELD * fld, char * data GCC_UNUSED)
5343 #define CHAR_CHECK_CB(func) int func(int ch, char *data GCC_UNUSED)
5344 #endif
5345
5346 /*
5347  * Allow a middle initial, optionally with a '.' to end it.
5348  */
5349 static
5350 FIELDCHECK_CB(mi_field_check)
5351 {
5352     char *s = field_buffer(fld, 0);
5353     int state = 0;
5354     int n;
5355
5356     for (n = 0; s[n] != '\0'; ++n) {
5357         switch (state) {
5358         case 0:
5359             if (s[n] == '.') {
5360                 if (n != 1)
5361                     return FALSE;
5362                 state = 2;
5363             } else if (isspace(UChar(s[n]))) {
5364                 state = 2;
5365             }
5366             break;
5367         case 2:
5368             if (!isspace(UChar(s[n])))
5369                 return FALSE;
5370             break;
5371         }
5372     }
5373
5374     /* force the form to display a leading capital */
5375     if (islower(UChar(s[0]))) {
5376         s[0] = toupper(UChar(s[0]));
5377         set_field_buffer(fld, 0, s);
5378     }
5379     return TRUE;
5380 }
5381
5382 static
5383 CHAR_CHECK_CB(mi_char_check)
5384 {
5385     return ((isalpha(ch) || ch == '.') ? TRUE : FALSE);
5386 }
5387
5388 /*
5389  * Passwords should be at least 6 characters.
5390  */
5391 static
5392 FIELDCHECK_CB(pw_field_check)
5393 {
5394     char *s = field_buffer(fld, 0);
5395     int n;
5396
5397     for (n = 0; s[n] != '\0'; ++n) {
5398         if (isspace(UChar(s[n]))) {
5399             if (n < 6)
5400                 return FALSE;
5401         }
5402     }
5403     return TRUE;
5404 }
5405
5406 static
5407 CHAR_CHECK_CB(pw_char_check)
5408 {
5409     return (isgraph(ch) ? TRUE : FALSE);
5410 }
5411
5412 static void
5413 demo_forms(void)
5414 {
5415     WINDOW *w;
5416     FORM *form;
5417     FIELD *f[12], *secure;
5418     FIELDTYPE *fty_middle = new_fieldtype(mi_field_check, mi_char_check);
5419     FIELDTYPE *fty_passwd = new_fieldtype(pw_field_check, pw_char_check);
5420     int finished = 0, c;
5421     unsigned n = 0;
5422
5423 #ifdef NCURSES_MOUSE_VERSION
5424     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
5425 #endif
5426
5427     move(18, 0);
5428     addstr("Defined edit/traversal keys:   ^Q/ESC- exit form\n");
5429     addstr("^N   -- go to next field       ^P  -- go to previous field\n");
5430     addstr("Home -- go to first field      End -- go to last field\n");
5431     addstr("^L   -- go to field to left    ^R  -- go to field to right\n");
5432     addstr("^U   -- move upward to field   ^D  -- move downward to field\n");
5433     addstr("^W   -- go to next word        ^B  -- go to previous word\n");
5434     addstr("^S   -- go to start of field   ^E  -- go to end of field\n");
5435     addstr("^H   -- delete previous char   ^Y  -- delete line\n");
5436     addstr("^G   -- delete current word    ^C  -- clear to end of line\n");
5437     addstr("^K   -- clear to end of field  ^X  -- clear field\n");
5438     addstr("Arrow keys move within a field as you would expect. ^] toggles overlay mode.");
5439
5440     mvaddstr(4, 57, "Forms Entry Test");
5441
5442     refresh();
5443
5444     /* describe the form */
5445     f[n++] = make_label(0, 15, "Sample Form");
5446
5447     f[n++] = make_label(2, 0, "Last Name");
5448     f[n++] = make_field(3, 0, 1, 18, FALSE);
5449     set_field_type(f[n - 1], TYPE_ALPHA, 1);
5450
5451     f[n++] = make_label(2, 20, "First Name");
5452     f[n++] = make_field(3, 20, 1, 12, FALSE);
5453     set_field_type(f[n - 1], TYPE_ALPHA, 1);
5454
5455     f[n++] = make_label(2, 34, "Middle Name");
5456     f[n++] = make_field(3, 34, 1, 12, FALSE);
5457     set_field_type(f[n - 1], fty_middle);
5458
5459     f[n++] = make_label(5, 0, "Comments");
5460     f[n++] = make_field(6, 0, 4, 46, FALSE);
5461
5462     f[n++] = make_label(5, 20, "Password:");
5463     secure =
5464         f[n++] = make_field(5, 30, 1, 9, TRUE);
5465     set_field_type(f[n - 1], fty_passwd);
5466     f[n++] = (FIELD *) 0;
5467
5468     form = new_form(f);
5469
5470     display_form(form);
5471
5472     w = form_win(form);
5473     raw();
5474     nonl();                     /* lets us read ^M's */
5475     while (!finished) {
5476         switch (form_driver(form, c = form_virtualize(form, w))) {
5477         case E_OK:
5478             mvaddstr(5, 57, field_buffer(secure, 1));
5479             clrtoeol();
5480             refresh();
5481             break;
5482         case E_UNKNOWN_COMMAND:
5483             finished = my_form_driver(form, c);
5484             break;
5485         default:
5486             beep();
5487             break;
5488         }
5489     }
5490
5491     erase_form(form);
5492
5493     free_form(form);
5494     for (c = 0; f[c] != 0; c++)
5495         free_field(f[c]);
5496     free_fieldtype(fty_middle);
5497     free_fieldtype(fty_passwd);
5498     noraw();
5499     nl();
5500
5501 #ifdef NCURSES_MOUSE_VERSION
5502     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
5503 #endif
5504 }
5505 #endif /* USE_LIBFORM */
5506
5507 /****************************************************************************
5508  *
5509  * Overlap test
5510  *
5511  ****************************************************************************/
5512
5513 static void
5514 fillwin(WINDOW *win, char ch)
5515 {
5516     int y, x;
5517     int y1, x1;
5518
5519     getmaxyx(win, y1, x1);
5520     for (y = 0; y < y1; y++) {
5521         wmove(win, y, 0);
5522         for (x = 0; x < x1; x++)
5523             waddch(win, UChar(ch));
5524     }
5525 }
5526
5527 static void
5528 crosswin(WINDOW *win, char ch)
5529 {
5530     int y, x;
5531     int y1, x1;
5532
5533     getmaxyx(win, y1, x1);
5534     for (y = 0; y < y1; y++) {
5535         for (x = 0; x < x1; x++)
5536             if (((x > (x1 - 1) / 3) && (x <= (2 * (x1 - 1)) / 3))
5537                 || (((y > (y1 - 1) / 3) && (y <= (2 * (y1 - 1)) / 3)))) {
5538                 wmove(win, y, x);
5539                 waddch(win, UChar(ch));
5540             }
5541     }
5542 }
5543
5544 #define OVERLAP_FLAVORS 5
5545
5546 static void
5547 overlap_helpitem(int state, int item, char *message)
5548 {
5549     int row = (item / 2);
5550     int col = ((item % 2) ? COLS / 2 : 0);
5551
5552     move(LINES - 6 + row, col);
5553     printw("%c%c = %s", state == row ? '>' : ' ', 'a' + item, message);
5554     clrtoeol();
5555 }
5556
5557 static void
5558 overlap_test_1_attr(WINDOW *win, int flavor, int col)
5559 {
5560     short cpair = 1 + (flavor * 2) + col;
5561
5562     switch (flavor) {
5563     case 0:
5564         wattrset(win, A_NORMAL);
5565         break;
5566     case 1:
5567         wattrset(win, A_BOLD);
5568         break;
5569     case 2:
5570         init_pair(cpair, COLOR_BLUE, COLOR_WHITE);
5571         wattrset(win, COLOR_PAIR(cpair) | A_NORMAL);
5572         break;
5573     case 3:
5574         init_pair(cpair, COLOR_WHITE, COLOR_BLUE);
5575         wattrset(win, COLOR_PAIR(cpair) | A_BOLD);
5576         break;
5577     }
5578 }
5579
5580 static void
5581 overlap_test_2_attr(WINDOW *win, int flavor, int col)
5582 {
5583     short cpair = 9 + (flavor * 2) + col;
5584
5585     switch (flavor) {
5586     case 0:
5587         /* no effect */
5588         break;
5589     case 1:
5590         /* no effect */
5591         break;
5592     case 2:
5593         init_pair(cpair, COLOR_RED, COLOR_GREEN);
5594         wbkgdset(win, ' ' | A_BLINK | COLOR_PAIR(cpair));
5595         break;
5596     case 3:
5597         wbkgdset(win, ' ' | A_NORMAL);
5598         break;
5599     }
5600 }
5601
5602 static int
5603 overlap_help(int state, int flavors[OVERLAP_FLAVORS])
5604 {
5605     int row;
5606     int col;
5607     int item;
5608     const char *ths, *tht;
5609     char msg[80];
5610
5611     if (state < 0)
5612         state += OVERLAP_FLAVORS;
5613     state = state % OVERLAP_FLAVORS;
5614
5615     for (item = 0; item < (2 * OVERLAP_FLAVORS); ++item) {
5616         row = item / 2;
5617         col = item % 2;
5618         ths = col ? "B" : "A";
5619         tht = col ? "A" : "B";
5620
5621         switch (row) {
5622         case 0:
5623             flavors[row] = 0;
5624             sprintf(msg, "refresh %s, then %s, then doupdate.", ths, tht);
5625             break;
5626         case 1:
5627             if (use_colors) {
5628                 flavors[row] %= 4;
5629             } else {
5630                 flavors[row] %= 2;
5631             }
5632             overlap_test_1_attr(stdscr, flavors[row], col);
5633             sprintf(msg, "fill window %s with letter %s.", ths, ths);
5634             break;
5635         case 2:
5636             if (use_colors) {
5637                 flavors[row] %= 4;
5638             } else {
5639                 flavors[row] %= 2;
5640             }
5641             switch (flavors[row]) {
5642             case 0:
5643                 sprintf(msg, "cross pattern in window %s.", ths);
5644                 break;
5645             case 1:
5646                 sprintf(msg, "draw box in window %s.", ths);
5647                 break;
5648             case 2:
5649                 sprintf(msg, "set background of window %s.", ths);
5650                 break;
5651             case 3:
5652                 sprintf(msg, "reset background of window %s.", ths);
5653                 break;
5654             }
5655             break;
5656         case 3:
5657             flavors[row] = 0;
5658             sprintf(msg, "clear window %s.", ths);
5659             break;
5660         case 4:
5661             flavors[row] %= 4;
5662             switch (flavors[row]) {
5663             case 0:
5664                 sprintf(msg, "overwrite %s onto %s.", ths, tht);
5665                 break;
5666             case 1:
5667                 sprintf(msg, "copywin(FALSE) %s onto %s.", ths, tht);
5668                 break;
5669             case 2:
5670                 sprintf(msg, "copywin(TRUE) %s onto %s.", ths, tht);
5671                 break;
5672             case 3:
5673                 sprintf(msg, "overlay %s onto %s.", ths, tht);
5674                 break;
5675             }
5676             break;
5677         }
5678         overlap_helpitem(state, item, msg);
5679         wattrset(stdscr, A_NORMAL);
5680         wbkgdset(stdscr, ' ' | A_NORMAL);
5681     }
5682     move(LINES - 1, 0);
5683     printw("^Q/ESC = terminate test.  Up/down/space select test variations (%d %d).",
5684            state, flavors[state]);
5685
5686     return state;
5687 }
5688
5689 static void
5690 overlap_test_0(WINDOW *a, WINDOW *b)
5691 {
5692     touchwin(a);
5693     touchwin(b);
5694     wnoutrefresh(a);
5695     wnoutrefresh(b);
5696     doupdate();
5697 }
5698
5699 static void
5700 overlap_test_1(int flavor, int col, WINDOW *a, char fill)
5701 {
5702     overlap_test_1_attr(a, flavor, col);
5703     fillwin(a, fill);
5704     wattrset(a, A_NORMAL);
5705 }
5706
5707 static void
5708 overlap_test_2(int flavor, int col, WINDOW *a, char fill)
5709 {
5710     overlap_test_2_attr(a, flavor, col);
5711     switch (flavor) {
5712     case 0:
5713         crosswin(a, fill);
5714         break;
5715     case 1:
5716         box(a, 0, 0);
5717         break;
5718     case 2:
5719         /* done in overlap_test_2_attr */
5720         break;
5721     case 3:
5722         /* done in overlap_test_2_attr */
5723         break;
5724     }
5725 }
5726
5727 static void
5728 overlap_test_3(WINDOW *a)
5729 {
5730     wclear(a);
5731     wmove(a, 0, 0);
5732 }
5733
5734 static void
5735 overlap_test_4(int flavor, WINDOW *a, WINDOW *b)
5736 {
5737     switch (flavor) {
5738     case 0:
5739         overwrite(a, b);
5740         break;
5741     case 1:
5742         copywin(a, b, 0, 0, 0, 0, getmaxy(b), getmaxx(b), FALSE);
5743         break;
5744     case 2:
5745         copywin(a, b, 0, 0, 0, 0, getmaxy(b), getmaxx(b), TRUE);
5746         break;
5747     case 3:
5748         overlay(a, b);
5749         break;
5750     }
5751 }
5752
5753 /* test effects of overlapping windows */
5754 static void
5755 overlap_test(void)
5756 {
5757     int ch;
5758     int state, flavor[OVERLAP_FLAVORS];
5759
5760     WINDOW *win1 = newwin(9, 20, 3, 3);
5761     WINDOW *win2 = newwin(9, 20, 9, 16);
5762
5763     curs_set(0);
5764     raw();
5765     refresh();
5766     move(0, 0);
5767     printw("This test shows the behavior of wnoutrefresh() with respect to\n");
5768     printw("the shared region of two overlapping windows A and B.  The cross\n");
5769     printw("pattern in each window does not overlap the other.\n");
5770
5771     memset(flavor, 0, sizeof(flavor));
5772     state = overlap_help(0, flavor);
5773
5774     while (!isQuit(ch = Getchar()))
5775         switch (ch) {
5776         case 'a':               /* refresh window A first, then B */
5777             overlap_test_0(win1, win2);
5778             break;
5779
5780         case 'b':               /* refresh window B first, then A */
5781             overlap_test_0(win2, win1);
5782             break;
5783
5784         case 'c':               /* fill window A so it's visible */
5785             overlap_test_1(flavor[1], 0, win1, 'A');
5786             break;
5787
5788         case 'd':               /* fill window B so it's visible */
5789             overlap_test_1(flavor[1], 1, win2, 'B');
5790             break;
5791
5792         case 'e':               /* cross test pattern in window A */
5793             overlap_test_2(flavor[2], 0, win1, 'A');
5794             break;
5795
5796         case 'f':               /* cross test pattern in window A */
5797             overlap_test_2(flavor[2], 1, win2, 'B');
5798             break;
5799
5800         case 'g':               /* clear window A */
5801             overlap_test_3(win1);
5802             break;
5803
5804         case 'h':               /* clear window B */
5805             overlap_test_3(win2);
5806             break;
5807
5808         case 'i':               /* overwrite A onto B */
5809             overlap_test_4(flavor[4], win1, win2);
5810             break;
5811
5812         case 'j':               /* overwrite B onto A */
5813             overlap_test_4(flavor[4], win2, win1);
5814             break;
5815
5816         case CTRL('n'):
5817         case KEY_DOWN:
5818             state = overlap_help(state + 1, flavor);
5819             break;
5820
5821         case CTRL('p'):
5822         case KEY_UP:
5823             state = overlap_help(state - 1, flavor);
5824             break;
5825
5826         case ' ':
5827             flavor[state] += 1;
5828             state = overlap_help(state, flavor);
5829             break;
5830
5831         case '?':
5832             state = overlap_help(state, flavor);
5833             break;
5834
5835         default:
5836             beep();
5837             break;
5838         }
5839
5840     delwin(win2);
5841     delwin(win1);
5842     erase();
5843     curs_set(1);
5844     endwin();
5845 }
5846
5847 /****************************************************************************
5848  *
5849  * Main sequence
5850  *
5851  ****************************************************************************/
5852
5853 static bool
5854 do_single_test(const char c)
5855 /* perform a single specified test */
5856 {
5857     switch (c) {
5858     case 'a':
5859         getch_test();
5860         break;
5861
5862 #if USE_WIDEC_SUPPORT
5863     case 'A':
5864         get_wch_test();
5865         break;
5866 #endif
5867
5868     case 'b':
5869         attr_test();
5870         break;
5871
5872 #if USE_WIDEC_SUPPORT
5873     case 'B':
5874         wide_attr_test();
5875         break;
5876 #endif
5877
5878     case 'c':
5879         if (!use_colors)
5880             Cannot("does not support color.");
5881         else
5882             color_test();
5883         break;
5884
5885 #if USE_WIDEC_SUPPORT
5886     case 'C':
5887         if (!use_colors)
5888             Cannot("does not support color.");
5889         else
5890             wide_color_test();
5891         break;
5892 #endif
5893
5894     case 'd':
5895         if (!use_colors)
5896             Cannot("does not support color.");
5897         else if (!can_change_color())
5898             Cannot("has hardwired color values.");
5899         else
5900             color_edit();
5901         break;
5902
5903 #if USE_SOFTKEYS
5904     case 'e':
5905         slk_test();
5906         break;
5907 #endif
5908
5909 #if USE_WIDEC_SUPPORT
5910     case 'E':
5911         wide_slk_test();
5912         break;
5913 #endif
5914     case 'f':
5915         acs_display();
5916         break;
5917
5918 #if USE_WIDEC_SUPPORT
5919     case 'F':
5920         wide_acs_display();
5921         break;
5922 #endif
5923
5924 #if USE_LIBPANEL
5925     case 'o':
5926         demo_panels(init_panel, fill_panel);
5927         break;
5928 #endif
5929
5930 #if USE_WIDEC_SUPPORT
5931     case 'O':
5932         demo_panels(init_wide_panel, fill_wide_panel);
5933         break;
5934 #endif
5935
5936     case 'g':
5937         acs_and_scroll();
5938         break;
5939
5940     case 'i':
5941         flushinp_test(stdscr);
5942         break;
5943
5944     case 'k':
5945         test_sgr_attributes();
5946         break;
5947
5948 #if USE_LIBMENU
5949     case 'm':
5950         menu_test();
5951         break;
5952 #endif
5953
5954     case 'p':
5955         demo_pad();
5956         break;
5957
5958 #if USE_LIBFORM
5959     case 'r':
5960         demo_forms();
5961         break;
5962 #endif
5963
5964     case 's':
5965         overlap_test();
5966         break;
5967
5968 #if USE_LIBMENU && defined(TRACE)
5969     case 't':
5970         trace_set();
5971         break;
5972 #endif
5973
5974     case '?':
5975         break;
5976
5977     default:
5978         return FALSE;
5979     }
5980
5981     return TRUE;
5982 }
5983
5984 static void
5985 usage(void)
5986 {
5987     static const char *const tbl[] =
5988     {
5989         "Usage: ncurses [options]"
5990         ,""
5991         ,"Options:"
5992 #ifdef NCURSES_VERSION
5993         ,"  -a f,b   set default-colors (assumed white-on-black)"
5994         ,"  -d       use default-colors if terminal supports them"
5995 #endif
5996 #if USE_SOFTKEYS
5997         ,"  -e fmt   specify format for soft-keys test (e)"
5998 #endif
5999 #if HAVE_RIPOFFLINE
6000         ,"  -f       rip-off footer line (can repeat)"
6001         ,"  -h       rip-off header line (can repeat)"
6002 #endif
6003         ,"  -m       do not use colors"
6004         ,"  -p file  rgb values to use in 'd' rather than ncurses's builtin"
6005 #if USE_LIBPANEL
6006         ,"  -s msec  specify nominal time for panel-demo (default: 1, to hold)"
6007 #endif
6008 #ifdef TRACE
6009         ,"  -t mask  specify default trace-level (may toggle with ^T)"
6010 #endif
6011     };
6012     size_t n;
6013     for (n = 0; n < SIZEOF(tbl); n++)
6014         fprintf(stderr, "%s\n", tbl[n]);
6015     ExitProgram(EXIT_FAILURE);
6016 }
6017
6018 static void
6019 set_terminal_modes(void)
6020 {
6021     noraw();
6022     cbreak();
6023     noecho();
6024     scrollok(stdscr, TRUE);
6025     idlok(stdscr, TRUE);
6026     keypad(stdscr, TRUE);
6027 }
6028
6029 #ifdef SIGUSR1
6030 static RETSIGTYPE
6031 announce_sig(int sig)
6032 {
6033     (void) fprintf(stderr, "Handled signal %d\r\n", sig);
6034 }
6035 #endif
6036
6037 #if HAVE_RIPOFFLINE
6038 static int
6039 rip_footer(WINDOW *win, int cols)
6040 {
6041     wbkgd(win, A_REVERSE);
6042     werase(win);
6043     wmove(win, 0, 0);
6044     wprintw(win, "footer: window %p, %d columns", win, cols);
6045     wnoutrefresh(win);
6046     return OK;
6047 }
6048
6049 static int
6050 rip_header(WINDOW *win, int cols)
6051 {
6052     wbkgd(win, A_REVERSE);
6053     werase(win);
6054     wmove(win, 0, 0);
6055     wprintw(win, "header: window %p, %d columns", win, cols);
6056     wnoutrefresh(win);
6057     return OK;
6058 }
6059 #endif /* HAVE_RIPOFFLINE */
6060
6061 static void
6062 main_menu(bool top)
6063 {
6064     char command;
6065
6066     do {
6067         (void) puts("This is the ncurses main menu");
6068         (void) puts("a = keyboard and mouse input test");
6069 #if USE_WIDEC_SUPPORT
6070         (void) puts("A = wide-character keyboard and mouse input test");
6071 #endif
6072         (void) puts("b = character attribute test");
6073 #if USE_WIDEC_SUPPORT
6074         (void) puts("B = wide-character attribute test");
6075 #endif
6076         (void) puts("c = color test pattern");
6077 #if USE_WIDEC_SUPPORT
6078         (void) puts("C = color test pattern using wide-character calls");
6079 #endif
6080         if (top)
6081             (void) puts("d = edit RGB color values");
6082 #if USE_SOFTKEYS
6083         (void) puts("e = exercise soft keys");
6084 #if USE_WIDEC_SUPPORT
6085         (void) puts("E = exercise soft keys using wide-characters");
6086 #endif
6087 #endif
6088         (void) puts("f = display ACS characters");
6089 #if USE_WIDEC_SUPPORT
6090         (void) puts("F = display Wide-ACS characters");
6091 #endif
6092         (void) puts("g = display windows and scrolling");
6093         (void) puts("i = test of flushinp()");
6094         (void) puts("k = display character attributes");
6095 #if USE_LIBMENU
6096         (void) puts("m = menu code test");
6097 #endif
6098 #if USE_LIBPANEL
6099         (void) puts("o = exercise panels library");
6100 #if USE_WIDEC_SUPPORT
6101         (void) puts("O = exercise panels with wide-characters");
6102 #endif
6103 #endif
6104         (void) puts("p = exercise pad features");
6105         (void) puts("q = quit");
6106 #if USE_LIBFORM
6107         (void) puts("r = exercise forms code");
6108 #endif
6109         (void) puts("s = overlapping-refresh test");
6110 #if USE_LIBMENU && defined(TRACE)
6111         (void) puts("t = set trace level");
6112 #endif
6113         (void) puts("? = repeat this command summary");
6114
6115         (void) fputs("> ", stdout);
6116         (void) fflush(stdout);  /* necessary under SVr4 curses */
6117
6118         /*
6119          * This used to be an 'fgets()' call.  However (on Linux, at least)
6120          * mixing stream I/O and 'read()' (used in the library) causes the
6121          * input stream to be flushed when switching between the two.
6122          */
6123         command = 0;
6124         for (;;) {
6125             char ch;
6126             if (read(fileno(stdin), &ch, 1) <= 0) {
6127                 if (command == 0)
6128                     command = 'q';
6129                 break;
6130             } else if (command == 0 && !isspace(UChar(ch))) {
6131                 command = ch;
6132             } else if (ch == '\n' || ch == '\r') {
6133                 if ((command == 'd') && !top) {
6134                     (void) fputs("Do not nest test-d\n", stdout);
6135                     command = 0;
6136                 }
6137                 if (command != 0)
6138                     break;
6139                 (void) fputs("> ", stdout);
6140                 (void) fflush(stdout);
6141             }
6142         }
6143
6144         if (do_single_test(command)) {
6145             /*
6146              * This may be overkill; it's intended to reset everything back
6147              * to the initial terminal modes so that tests don't get in
6148              * each other's way.
6149              */
6150             flushinp();
6151             set_terminal_modes();
6152             reset_prog_mode();
6153             clear();
6154             refresh();
6155             endwin();
6156             if (command == '?') {
6157                 (void) puts("This is the ncurses capability tester.");
6158                 (void)
6159                     puts("You may select a test from the main menu by typing the");
6160                 (void)
6161                     puts("key letter of the choice (the letter to left of the =)");
6162                 (void)
6163                     puts("at the > prompt.  Type `q' to exit.");
6164             }
6165             continue;
6166         }
6167     } while
6168         (command != 'q');
6169 }
6170
6171 /*+-------------------------------------------------------------------------
6172         main(argc,argv)
6173 --------------------------------------------------------------------------*/
6174
6175 #define okCOLOR(n) ((n) >= 0 && (n) < max_colors)
6176 #define okRGB(n)   ((n) >= 0 && (n) <= 1000)
6177
6178 int
6179 main(int argc, char *argv[])
6180 {
6181     int c;
6182     int my_e_param = 1;
6183 #ifdef NCURSES_VERSION
6184     int default_fg = COLOR_WHITE;
6185     int default_bg = COLOR_BLACK;
6186     bool assumed_colors = FALSE;
6187     bool default_colors = FALSE;
6188 #endif
6189     char *palette_file = 0;
6190     bool monochrome = FALSE;
6191
6192     setlocale(LC_ALL, "");
6193
6194     while ((c = getopt(argc, argv, "a:de:fhmp:s:t:")) != -1) {
6195         switch (c) {
6196 #ifdef NCURSES_VERSION
6197         case 'a':
6198             assumed_colors = TRUE;
6199             sscanf(optarg, "%d,%d", &default_fg, &default_bg);
6200             break;
6201         case 'd':
6202             default_colors = TRUE;
6203             break;
6204 #endif
6205         case 'e':
6206             my_e_param = atoi(optarg);
6207 #ifdef NCURSES_VERSION
6208             if (my_e_param > 3) /* allow extended layouts */
6209                 usage();
6210 #else
6211             if (my_e_param > 1)
6212                 usage();
6213 #endif
6214             break;
6215 #if HAVE_RIPOFFLINE
6216         case 'f':
6217             ripoffline(-1, rip_footer);
6218             break;
6219         case 'h':
6220             ripoffline(1, rip_header);
6221             break;
6222 #endif /* HAVE_RIPOFFLINE */
6223         case 'm':
6224             monochrome = TRUE;
6225             break;
6226         case 'p':
6227             palette_file = optarg;
6228             break;
6229 #if USE_LIBPANEL
6230         case 's':
6231             nap_msec = atol(optarg);
6232             break;
6233 #endif
6234 #ifdef TRACE
6235         case 't':
6236             save_trace = strtol(optarg, 0, 0);
6237             break;
6238 #endif
6239         default:
6240             usage();
6241         }
6242     }
6243
6244     /*
6245      * If there's no menus (unlikely for ncurses!), then we'll have to set
6246      * tracing on initially, just in case the user wants to test something that
6247      * doesn't involve wGetchar.
6248      */
6249 #ifdef TRACE
6250     /* enable debugging */
6251 #if !USE_LIBMENU
6252     trace(save_trace);
6253 #else
6254     if (!isatty(fileno(stdin)))
6255         trace(save_trace);
6256 #endif /* USE_LIBMENU */
6257 #endif /* TRACE */
6258
6259 #if USE_SOFTKEYS
6260     /* tell it we're going to play with soft keys */
6261     slk_init(my_e_param);
6262 #endif
6263
6264 #ifdef SIGUSR1
6265     /* set up null signal catcher so we can see what interrupts to getch do */
6266     signal(SIGUSR1, announce_sig);
6267 #endif
6268
6269     /* we must initialize the curses data structure only once */
6270     initscr();
6271     bkgdset(BLANK);
6272
6273     /* tests, in general, will want these modes */
6274     use_colors = monochrome ? FALSE : has_colors();
6275
6276     if (use_colors) {
6277         start_color();
6278 #ifdef NCURSES_VERSION_PATCH
6279         max_colors = COLORS;    /* was > 16 ? 16 : COLORS */
6280 #if HAVE_USE_DEFAULT_COLORS
6281         if (default_colors) {
6282             use_default_colors();
6283             min_colors = -1;
6284         }
6285 #if NCURSES_VERSION_PATCH >= 20000708
6286         else if (assumed_colors)
6287             assume_default_colors(default_fg, default_bg);
6288 #endif
6289 #endif
6290 #else /* normal SVr4 curses */
6291         max_colors = COLORS;    /* was > 8 ? 8 : COLORS */
6292 #endif
6293         max_pairs = COLOR_PAIRS;        /* was > 256 ? 256 : COLOR_PAIRS */
6294
6295         if (can_change_color()) {
6296             short cp;
6297             all_colors = (RGB_DATA *) malloc(max_colors * sizeof(RGB_DATA));
6298             for (cp = 0; cp < max_colors; ++cp) {
6299                 color_content(cp,
6300                               &all_colors[cp].red,
6301                               &all_colors[cp].green,
6302                               &all_colors[cp].blue);
6303             }
6304             if (palette_file != 0) {
6305                 FILE *fp = fopen(palette_file, "r");
6306                 if (fp != 0) {
6307                     char buffer[BUFSIZ];
6308                     int red, green, blue;
6309                     int scale = 1000;
6310                     while (fgets(buffer, sizeof(buffer), fp) != 0) {
6311                         if (sscanf(buffer, "scale:%d", &c) == 1) {
6312                             scale = c;
6313                         } else if (sscanf(buffer, "%d:%d %d %d",
6314                                           &c,
6315                                           &red,
6316                                           &green,
6317                                           &blue) == 4
6318                                    && okCOLOR(c)
6319                                    && okRGB(red)
6320                                    && okRGB(green)
6321                                    && okRGB(blue)) {
6322                             all_colors[c].red = (red * 1000) / scale;
6323                             all_colors[c].green = (green * 1000) / scale;
6324                             all_colors[c].blue = (blue * 1000) / scale;
6325                         }
6326                     }
6327                     fclose(fp);
6328                 }
6329             }
6330         }
6331     }
6332     set_terminal_modes();
6333     def_prog_mode();
6334
6335     /*
6336      * Return to terminal mode, so we're guaranteed of being able to
6337      * select terminal commands even if the capabilities are wrong.
6338      */
6339     endwin();
6340
6341 #if HAVE_CURSES_VERSION
6342     (void) printf("Welcome to %s.  Press ? for help.\n", curses_version());
6343 #elif defined(NCURSES_VERSION_MAJOR) && defined(NCURSES_VERSION_MINOR) && defined(NCURSES_VERSION_PATCH)
6344     (void) printf("Welcome to ncurses %d.%d.%d.  Press ? for help.\n",
6345                   NCURSES_VERSION_MAJOR,
6346                   NCURSES_VERSION_MINOR,
6347                   NCURSES_VERSION_PATCH);
6348 #else
6349     (void) puts("Welcome to ncurses.  Press ? for help.");
6350 #endif
6351
6352     main_menu(TRUE);
6353
6354     ExitProgram(EXIT_SUCCESS);
6355 }
6356
6357 /* ncurses.c ends here */