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