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