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