]> ncurses.scripts.mit.edu Git - ncurses.git/blob - test/ncurses.c
ncurses 6.1 - patch 20190928
[ncurses.git] / test / ncurses.c
1 /****************************************************************************
2  * Copyright (c) 1998-2018,2019 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.519 2019/09/22 19:12:40 tom Exp $
44
45 ***************************************************************************/
46
47 #include <test.priv.h>
48
49 #ifdef __hpux
50 #undef mvwdelch                 /* HPUX 11.23 macro will not compile */
51 #endif
52
53 #if HAVE_GETTIMEOFDAY
54 #if HAVE_SYS_TIME_H && HAVE_SYS_TIME_SELECT
55 #include <sys/time.h>
56 #endif
57 #if HAVE_SYS_SELECT_H
58 #include <sys/select.h>
59 #endif
60 #endif
61
62 #if USE_LIBPANEL
63 #include <panel.h>
64 #endif
65
66 #if USE_LIBMENU
67 #include <menu.h>
68 #endif
69
70 #if USE_LIBFORM
71 #include <form.h>
72 #endif
73
74 #ifdef NCURSES_VERSION
75
76 #define NCURSES_CONST_PARAM const void
77
78 #ifdef TRACE
79 static unsigned save_trace = TRACE_ORDINARY | TRACE_ICALLS | TRACE_CALLS;
80 #endif
81
82 #else
83
84 #define NCURSES_CONST_PARAM char
85
86 #define mmask_t chtype          /* not specified in XSI */
87
88 #ifndef ACS_S3
89 #ifdef CURSES_ACS_ARRAY
90 #define ACS_S3          (CURSES_ACS_ARRAY['p'])         /* scan line 3 */
91 #define ACS_S7          (CURSES_ACS_ARRAY['r'])         /* scan line 7 */
92 #define ACS_LEQUAL      (CURSES_ACS_ARRAY['y'])         /* less/equal */
93 #define ACS_GEQUAL      (CURSES_ACS_ARRAY['z'])         /* greater/equal */
94 #define ACS_PI          (CURSES_ACS_ARRAY['{'])         /* Pi */
95 #define ACS_NEQUAL      (CURSES_ACS_ARRAY['|'])         /* not equal */
96 #define ACS_STERLING    (CURSES_ACS_ARRAY['}'])         /* UK pound sign */
97 #else
98 #define ACS_S3          (A_ALTCHARSET + 'p')    /* scan line 3 */
99 #define ACS_S7          (A_ALTCHARSET + 'r')    /* scan line 7 */
100 #define ACS_LEQUAL      (A_ALTCHARSET + 'y')    /* less/equal */
101 #define ACS_GEQUAL      (A_ALTCHARSET + 'z')    /* greater/equal */
102 #define ACS_PI          (A_ALTCHARSET + '{')    /* Pi */
103 #define ACS_NEQUAL      (A_ALTCHARSET + '|')    /* not equal */
104 #define ACS_STERLING    (A_ALTCHARSET + '}')    /* UK pound sign */
105 #endif
106 #endif /* ACS_S3 */
107
108 #ifndef WACS_S3
109 #ifdef CURSES_WACS_ARRAY
110 #define WACS_S3         (&(CURSES_WACS_ARRAY['p']))     /* scan line 3 */
111 #define WACS_S7         (&(CURSES_WACS_ARRAY['r']))     /* scan line 7 */
112 #define WACS_LEQUAL     (&(CURSES_WACS_ARRAY['y']))     /* less/equal */
113 #define WACS_GEQUAL     (&(CURSES_WACS_ARRAY['z']))     /* greater/equal */
114 #define WACS_PI         (&(CURSES_WACS_ARRAY['{']))     /* Pi */
115 #define WACS_NEQUAL     (&(CURSES_WACS_ARRAY['|']))     /* not equal */
116 #define WACS_STERLING   (&(CURSES_WACS_ARRAY['}']))     /* UK pound sign */
117 #endif
118 #endif
119
120 #endif
121
122 #if HAVE_WCSRTOMBS
123 #define count_wchars(src, len, state)      wcsrtombs(0,   &src, len, state)
124 #define trans_wchars(dst, src, len, state) wcsrtombs(dst, &src, len, state)
125 #define reset_wchars(state) init_mb(state)
126 #elif HAVE_WCSTOMBS && HAVE_MBTOWC && HAVE_MBLEN
127 #define count_wchars(src, len, state)      wcstombs(0,   src, len)
128 #define trans_wchars(dst, src, len, state) wcstombs(dst, src, len)
129 #define reset_wchars(state) IGNORE_RC(mblen(NULL, 0)), IGNORE_RC(mbtowc(NULL, NULL, 0))
130 #define state_unused
131 #endif
132
133 #if HAVE_MBSRTOWCS
134 #define count_mbytes(src, len, state)      mbsrtowcs(0,   &src, len, state)
135 #define trans_mbytes(dst, src, len, state) mbsrtowcs(dst, &src, len, state)
136 #define reset_mbytes(state) init_mb(state)
137 #elif HAVE_MBSTOWCS && HAVE_MBTOWC && HAVE_MBLEN
138 #define count_mbytes(src, len, state)      mbstowcs(0,   src, len)
139 #define trans_mbytes(dst, src, len, state) mbstowcs(dst, src, len)
140 #define reset_mbytes(state) IGNORE_RC(mblen(NULL, 0)), IGNORE_RC(mbtowc(NULL, NULL, 0))
141 #define state_unused
142 #endif
143
144 #define ToggleAcs(temp,real) temp = ((temp == real) ? NULL : real)
145
146 #define P(string)       printw("%s\n", string)
147
148 #define BLANK           ' '     /* this is the background character */
149
150 static int MaxColors;           /* the actual number of colors we'll use */
151 static int MinColors;           /* the minimum color code */
152 static bool UseColors;          /* true if we use colors */
153
154 #undef max_pairs
155 static int max_pairs;           /* ...and the number of color pairs */
156
157 #if HAVE_COLOR_CONTENT
158 typedef struct {
159     NCURSES_COLOR_T red;
160     NCURSES_COLOR_T green;
161     NCURSES_COLOR_T blue;
162 } RGB_DATA;
163
164 static RGB_DATA *all_colors;
165 #endif
166
167 static void main_menu(bool);
168 static void failed(const char *s) GCC_NORETURN;
169
170 static void
171 failed(const char *s)
172 {
173     perror(s);
174     endwin();
175     ExitProgram(EXIT_FAILURE);
176 }
177
178 static void
179 Repaint(void)
180 {
181     touchwin(stdscr);
182 #if HAVE_CURSCR
183     touchwin(curscr);
184     wrefresh(curscr);
185 #else
186     wrefresh(stdscr);
187 #endif
188 }
189
190 static bool
191 isQuit(int c, bool escape)
192 {
193     return ((c) == QUIT || (escape && ((c) == ESCAPE)));
194 }
195 #define case_QUIT       QUIT: case ESCAPE
196
197 /* Common function to allow ^T to toggle trace-mode in the middle of a test
198  * so that trace-files can be made smaller.
199  */
200 static int
201 wGetchar(WINDOW *win)
202 {
203     int c;
204 #ifdef TRACE
205     while ((c = wgetch(win)) == CTRL('T')) {
206         if (_nc_tracing) {
207             save_trace = _nc_tracing;
208             Trace(("TOGGLE-TRACING OFF"));
209             _nc_tracing = 0;
210         } else {
211             _nc_tracing = save_trace;
212         }
213         trace(_nc_tracing);
214         if (_nc_tracing)
215             Trace(("TOGGLE-TRACING ON"));
216     }
217 #else
218     c = wgetch(win);
219 #endif
220     return c;
221 }
222 #define Getchar() wGetchar(stdscr)
223
224 #if USE_SOFTKEYS
225 /* replaces wgetnstr(), since we want to be able to edit values */
226 static void
227 wGetstring(WINDOW *win, char *buffer, int limit)
228 {
229     int y0, x0, x;
230     bool done = FALSE;
231
232     echo();
233     getyx(win, y0, x0);
234     (void) wattrset(win, A_REVERSE);
235
236     x = (int) strlen(buffer);
237     while (!done) {
238         int ch;
239         if (x > (int) strlen(buffer))
240             x = (int) strlen(buffer);
241         wmove(win, y0, x0);
242         wprintw(win, "%-*s", limit, buffer);
243         wmove(win, y0, x0 + x);
244         switch (ch = wGetchar(win)) {
245         case '\n':
246         case KEY_ENTER:
247             done = TRUE;
248             break;
249         case CTRL('U'):
250             *buffer = '\0';
251             break;
252         case '\b':
253         case KEY_BACKSPACE:
254         case KEY_DC:
255             if (x > 0) {
256                 int j;
257                 for (j = --x; (buffer[j] = buffer[j + 1]) != '\0'; ++j) {
258                     ;
259                 }
260             } else {
261                 beep();
262             }
263             break;
264         case KEY_LEFT:
265             if (x > 0) {
266                 --x;
267             } else {
268                 flash();
269             }
270             break;
271         case KEY_RIGHT:
272             ++x;
273             break;
274         default:
275             if (!isprint(ch) || ch >= KEY_MIN) {
276                 beep();
277             } else if ((int) strlen(buffer) < limit) {
278                 int j;
279                 for (j = (int) strlen(buffer) + 1; j > x; --j) {
280                     buffer[j] = buffer[j - 1];
281                 }
282                 buffer[x++] = (char) ch;
283             } else {
284                 flash();
285             }
286         }
287     }
288
289     wattroff(win, A_REVERSE);
290     wmove(win, y0, x0);
291     noecho();
292 }
293 #endif
294
295 #if USE_WIDEC_SUPPORT
296 static wchar_t
297 fullwidth_digit(int ch)
298 {
299     return (wchar_t) (ch + 0xff10 - '0');
300 }
301
302 static void
303 make_fullwidth_text(wchar_t *target, const char *source)
304 {
305     int ch;
306     while ((ch = *source++) != 0) {
307         *target++ = fullwidth_digit(ch);
308     }
309     *target = 0;
310 }
311
312 static void
313 make_narrow_text(wchar_t *target, const char *source)
314 {
315     int ch;
316     while ((ch = *source++) != 0) {
317         *target++ = (wchar_t) ch;
318     }
319     *target = 0;
320 }
321
322 #if USE_LIBPANEL
323 static void
324 make_fullwidth_digit(cchar_t *target, int digit)
325 {
326     wchar_t source[2];
327
328     source[0] = fullwidth_digit(digit + '0');
329     source[1] = 0;
330     setcchar(target, source, A_NORMAL, 0, 0);
331 }
332 #endif
333
334 static int
335 wGet_wchar(WINDOW *win, wint_t *result)
336 {
337     int c;
338 #ifdef TRACE
339     while ((c = wget_wch(win, result)) == CTRL('T')) {
340         if (_nc_tracing) {
341             save_trace = _nc_tracing;
342             Trace(("TOGGLE-TRACING OFF"));
343             _nc_tracing = 0;
344         } else {
345             _nc_tracing = save_trace;
346         }
347         trace(_nc_tracing);
348         if (_nc_tracing)
349             Trace(("TOGGLE-TRACING ON"));
350     }
351 #else
352     c = wget_wch(win, result);
353 #endif
354     return c;
355 }
356 #define Get_wchar(result) wGet_wchar(stdscr, result)
357
358 /* replaces wgetn_wstr(), since we want to be able to edit values */
359 #if USE_SOFTKEYS
360 static void
361 wGet_wstring(WINDOW *win, wchar_t *buffer, int limit)
362 {
363     int y0, x0, x;
364     wint_t ch;
365     bool done = FALSE;
366     bool fkey = FALSE;
367
368     echo();
369     getyx(win, y0, x0);
370     (void) wattrset(win, A_REVERSE);
371
372     x = (int) wcslen(buffer);
373     while (!done) {
374         if (x > (int) wcslen(buffer))
375             x = (int) wcslen(buffer);
376
377         /* clear the "window' */
378         wmove(win, y0, x0);
379         wprintw(win, "%*s", limit, " ");
380
381         /* write the existing buffer contents */
382         wmove(win, y0, x0);
383         waddnwstr(win, buffer, limit);
384
385         /* positions the cursor past character 'x' */
386         wmove(win, y0, x0);
387         waddnwstr(win, buffer, x);
388
389         switch (wGet_wchar(win, &ch)) {
390         case KEY_CODE_YES:
391             fkey = TRUE;
392             switch (ch) {
393             case KEY_ENTER:
394                 ch = '\n';
395                 fkey = FALSE;
396                 break;
397             case KEY_BACKSPACE:
398             case KEY_DC:
399                 ch = '\b';
400                 fkey = FALSE;
401                 break;
402             case KEY_LEFT:
403             case KEY_RIGHT:
404                 break;
405             default:
406                 ch = (wint_t) -1;
407                 break;
408             }
409             break;
410         case OK:
411             fkey = FALSE;
412             break;
413         default:
414             ch = (wint_t) -1;
415             fkey = TRUE;
416             break;
417         }
418
419         switch (ch) {
420         case '\n':
421             done = TRUE;
422             break;
423         case CTRL('U'):
424             *buffer = '\0';
425             break;
426         case '\b':
427             if (x > 0) {
428                 int j;
429                 for (j = --x; (buffer[j] = buffer[j + 1]) != '\0'; ++j) {
430                     ;
431                 }
432             } else {
433                 beep();
434             }
435             break;
436         case KEY_LEFT:
437             if (x > 0) {
438                 --x;
439             } else {
440                 beep();
441             }
442             break;
443         case KEY_RIGHT:
444             ++x;
445             break;
446         default:
447             if (fkey) {
448                 beep();
449             } else if ((int) wcslen(buffer) < limit) {
450                 int j;
451                 for (j = (int) wcslen(buffer) + 1; j > x; --j) {
452                     buffer[j] = buffer[j - 1];
453                 }
454                 buffer[x++] = (wchar_t) ch;
455             } else {
456                 beep();
457             }
458         }
459     }
460
461     wattroff(win, A_REVERSE);
462     wmove(win, y0, x0);
463     noecho();
464 }
465 #endif /* USE_SOFTKEYS */
466
467 #endif /* USE_WIDEC_SUPPORT */
468
469 static void
470 Pause(void)
471 {
472     move(LINES - 1, 0);
473     addstr("Press any key to continue... ");
474     (void) Getchar();
475 }
476
477 static void
478 Cannot(const char *what)
479 {
480     printw("\nThis %s terminal %s\n\n", getenv("TERM"), what);
481     Pause();
482     endwin();
483 }
484
485 static void
486 ShellOut(bool message)
487 {
488     if (message)
489         addstr("Shelling out...");
490     def_prog_mode();
491     endwin();
492 #ifdef _WIN32
493     system("cmd.exe");
494 #else
495     IGNORE_RC(system("sh"));
496 #endif
497     if (message)
498         addstr("returned from shellout.\n");
499     refresh();
500 }
501
502 #ifdef NCURSES_MOUSE_VERSION
503 /*
504  * This function is the same as _tracemouse(), but we cannot count on that
505  * being available in the non-debug library.
506  */
507 static const char *
508 mouse_decode(MEVENT const *ep)
509 {
510     static char buf[80 + (5 * 10) + (32 * 15)];
511
512     (void) _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf))
513                        "id %2d at (%2d, %2d, %d) state %4lx = {",
514                        ep->id, ep->x, ep->y, ep->z, (unsigned long) ep->bstate);
515
516 #define SHOW(m, s) \
517         if ((ep->bstate & m)==m) { \
518                 _nc_STRCAT(buf, s, sizeof(buf)); \
519                 _nc_STRCAT(buf, ", ", sizeof(buf)); \
520         }
521
522     SHOW(BUTTON1_RELEASED, "release-1");
523     SHOW(BUTTON1_PRESSED, "press-1");
524     SHOW(BUTTON1_CLICKED, "click-1");
525     SHOW(BUTTON1_DOUBLE_CLICKED, "doubleclick-1");
526     SHOW(BUTTON1_TRIPLE_CLICKED, "tripleclick-1");
527 #if NCURSES_MOUSE_VERSION == 1
528     SHOW(BUTTON1_RESERVED_EVENT, "reserved-1");
529 #endif
530
531     SHOW(BUTTON2_RELEASED, "release-2");
532     SHOW(BUTTON2_PRESSED, "press-2");
533     SHOW(BUTTON2_CLICKED, "click-2");
534     SHOW(BUTTON2_DOUBLE_CLICKED, "doubleclick-2");
535     SHOW(BUTTON2_TRIPLE_CLICKED, "tripleclick-2");
536 #if NCURSES_MOUSE_VERSION == 1
537     SHOW(BUTTON2_RESERVED_EVENT, "reserved-2");
538 #endif
539
540     SHOW(BUTTON3_RELEASED, "release-3");
541     SHOW(BUTTON3_PRESSED, "press-3");
542     SHOW(BUTTON3_CLICKED, "click-3");
543     SHOW(BUTTON3_DOUBLE_CLICKED, "doubleclick-3");
544     SHOW(BUTTON3_TRIPLE_CLICKED, "tripleclick-3");
545 #if NCURSES_MOUSE_VERSION == 1
546     SHOW(BUTTON3_RESERVED_EVENT, "reserved-3");
547 #endif
548
549     SHOW(BUTTON4_RELEASED, "release-4");
550     SHOW(BUTTON4_PRESSED, "press-4");
551     SHOW(BUTTON4_CLICKED, "click-4");
552     SHOW(BUTTON4_DOUBLE_CLICKED, "doubleclick-4");
553     SHOW(BUTTON4_TRIPLE_CLICKED, "tripleclick-4");
554 #if NCURSES_MOUSE_VERSION == 1
555     SHOW(BUTTON4_RESERVED_EVENT, "reserved-4");
556 #endif
557
558 #if NCURSES_MOUSE_VERSION == 2
559     SHOW(BUTTON5_RELEASED, "release-5");
560     SHOW(BUTTON5_PRESSED, "press-5");
561     SHOW(BUTTON5_CLICKED, "click-5");
562     SHOW(BUTTON5_DOUBLE_CLICKED, "doubleclick-5");
563     SHOW(BUTTON5_TRIPLE_CLICKED, "tripleclick-5");
564 #endif
565
566     SHOW(BUTTON_CTRL, "ctrl");
567     SHOW(BUTTON_SHIFT, "shift");
568     SHOW(BUTTON_ALT, "alt");
569     SHOW(ALL_MOUSE_EVENTS, "all-events");
570     SHOW(REPORT_MOUSE_POSITION, "position");
571
572 #undef SHOW
573
574     if (buf[strlen(buf) - 1] == ' ')
575         buf[strlen(buf) - 2] = '\0';
576     _nc_STRCAT(buf, "}", sizeof(buf));
577     return (buf);
578 }
579
580 static void
581 show_mouse(WINDOW *win)
582 {
583     MEVENT event;
584     bool outside;
585     bool show_loc;
586
587     getmouse(&event);
588     outside = !wenclose(win, event.y, event.x);
589
590     if (outside) {
591         (void) wstandout(win);
592         waddstr(win, "KEY_MOUSE");
593         (void) wstandend(win);
594     } else {
595         waddstr(win, "KEY_MOUSE");
596     }
597     wprintw(win, ", %s", mouse_decode(&event));
598
599     if (outside)
600         win = stdscr;
601
602     show_loc = wmouse_trafo(win, &event.y, &event.x, FALSE);
603
604     if (show_loc) {
605         int y, x;
606         getyx(win, y, x);
607         wmove(win, event.y, event.x);
608         waddch(win, '*');
609         wmove(win, y, x);
610     }
611
612     if (outside)
613         wnoutrefresh(win);
614 }
615 #endif /* NCURSES_MOUSE_VERSION */
616
617 /****************************************************************************
618  *
619  * Character input test
620  *
621  ****************************************************************************/
622
623 #define NUM_GETCH_FLAGS 256
624 typedef bool GetchFlags[NUM_GETCH_FLAGS];
625
626 static void
627 setup_getch(WINDOW *win, GetchFlags flags)
628 {
629     keypad(win, flags['k']);    /* should be redundant, but for testing */
630     meta(win, flags['m']);      /* force this to a known state */
631     if (flags['e'])
632         echo();
633     else
634         noecho();
635 }
636
637 static void
638 init_getch(WINDOW *win, GetchFlags flags, int delay)
639 {
640     memset(flags, FALSE, NUM_GETCH_FLAGS);
641     flags[UChar('k')] = (win == stdscr);
642     flags[UChar('m')] = TRUE;
643     flags[UChar('t')] = (delay != 0);
644
645     setup_getch(win, flags);
646 }
647
648 static bool
649 blocking_getch(GetchFlags flags, int delay)
650 {
651     return ((delay < 0) && flags['t']);
652 }
653
654 #define ExitOnEscape() (flags[UChar('k')] && flags[UChar('t')])
655
656 static void
657 wgetch_help(WINDOW *win, GetchFlags flags)
658 {
659     static const char *help[] =
660     {
661         "e  -- toggle echo mode"
662         ,"g  -- triggers a getstr test"
663         ,"k  -- toggle keypad/literal mode"
664         ,"m  -- toggle meta (7-bit/8-bit) mode"
665         ,"^q -- quit"
666         ,"s  -- shell out"
667         ,"t  -- toggle timeout"
668         ,"w  -- create a new window"
669 #ifdef SIGTSTP
670         ,"z  -- suspend this process"
671 #endif
672     };
673     int y, x;
674     unsigned chk = ((SIZEOF(help) + 1) / 2);
675     unsigned n;
676
677     getyx(win, y, x);
678     move(0, 0);
679     printw("Type any key to see its %s value.  Also:\n",
680            flags['k'] ? "keypad" : "literal");
681     for (n = 0; n < SIZEOF(help); ++n) {
682         const char *msg = help[n];
683         int row = 1 + (int) (n % chk);
684         int col = (n >= chk) ? COLS / 2 : 0;
685         int flg = ((strstr(msg, "toggle") != 0)
686                    && (flags[UChar(*msg)] != FALSE));
687         if (*msg == '^' && ExitOnEscape())
688             msg = "^[,^q -- quit";
689         if (flg)
690             (void) standout();
691         MvPrintw(row, col, "%s", msg);
692         if (col == 0)
693             clrtoeol();
694         if (flg)
695             (void) standend();
696     }
697     wrefresh(stdscr);
698     wmove(win, y, x);
699 }
700
701 static void
702 wgetch_wrap(WINDOW *win, int first_y)
703 {
704     int last_y = getmaxy(win) - 1;
705     int y = getcury(win) + 1;
706
707     if (y >= last_y)
708         y = first_y;
709     wmove(win, y, 0);
710     wclrtoeol(win);
711 }
712
713 #if defined(KEY_RESIZE) && HAVE_WRESIZE
714 typedef struct {
715     WINDOW *text;
716     WINDOW *frame;
717 } WINSTACK;
718
719 static WINSTACK *winstack = 0;
720 static unsigned len_winstack = 0;
721
722 static void
723 forget_boxes(void)
724 {
725     if (winstack != 0) {
726         free(winstack);
727     }
728     winstack = 0;
729     len_winstack = 0;
730 }
731
732 static void
733 remember_boxes(unsigned level, WINDOW *txt_win, WINDOW *box_win)
734 {
735     unsigned need = (level + 1) * 2;
736
737     assert(level < (unsigned) COLS);
738
739     if (winstack == 0) {
740         len_winstack = 20;
741         winstack = typeMalloc(WINSTACK, len_winstack);
742     } else if (need >= len_winstack) {
743         len_winstack = need;
744         winstack = typeRealloc(WINSTACK, len_winstack, winstack);
745     }
746     if (!winstack)
747         failed("remember_boxes");
748     winstack[level].text = txt_win;
749     winstack[level].frame = box_win;
750 }
751
752 #if USE_SOFTKEYS && (defined(NCURSES_VERSION_PATCH) && NCURSES_VERSION_PATCH < 20071229) && NCURSES_EXT_FUNCS
753 static void
754 slk_repaint(void)
755 {
756     /* this chunk is now done in resize_term() */
757     slk_touch();
758     slk_clear();
759     slk_noutrefresh();
760 }
761
762 #else
763 #define slk_repaint()           /* nothing */
764 #endif
765
766 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
767 /*
768  * For wgetch_test(), we create pairs of windows - one for a box, one for text.
769  * Resize both and paint the box in the parent.
770  */
771 static void
772 resize_boxes(unsigned level, WINDOW *win)
773 {
774     unsigned n;
775     int base = 5;
776     int high = LINES - base;
777     int wide = COLS;
778
779     touchwin(stdscr);
780     wnoutrefresh(stdscr);
781
782     slk_repaint();
783
784     for (n = 0; n < level; ++n) {
785         wresize(winstack[n].frame, high, wide);
786         wresize(winstack[n].text, high - 2, wide - 2);
787         high -= 2;
788         wide -= 2;
789         werase(winstack[n].text);
790         box(winstack[n].frame, 0, 0);
791         wnoutrefresh(winstack[n].frame);
792         wprintw(winstack[n].text,
793                 "size %dx%d\n",
794                 getmaxy(winstack[n].text),
795                 getmaxx(winstack[n].text));
796         wnoutrefresh(winstack[n].text);
797         if (winstack[n].text == win)
798             break;
799     }
800     doupdate();
801 }
802 #endif /* resize_boxes */
803 #else
804 #define forget_boxes()          /* nothing */
805 #define remember_boxes(level,text,frame)        /* nothing */
806 #endif
807
808 /*
809  * Return-code is OK/ERR or a keyname.
810  */
811 static const char *
812 ok_keyname(int code)
813 {
814     return ((code == OK) ? "OK" : ((code == ERR) ? "ERR" : keyname(code)));
815 }
816
817 static void
818 wgetch_test(unsigned level, WINDOW *win, int delay)
819 {
820     char buf[BUFSIZ];
821     int first_y, first_x;
822     int incount = 0;
823     GetchFlags flags;
824
825     init_getch(win, flags, delay);
826     notimeout(win, FALSE);
827     wtimeout(win, delay);
828     getyx(win, first_y, first_x);
829
830     wgetch_help(win, flags);
831     wsetscrreg(win, first_y, getmaxy(win) - 1);
832     scrollok(win, TRUE);
833
834     for (;;) {
835         int c;
836
837         while ((c = wGetchar(win)) == ERR) {
838             incount++;
839             if (blocking_getch(flags, delay)) {
840                 (void) wprintw(win, "%05d: input error", incount);
841                 break;
842             } else {
843                 (void) wprintw(win, "%05d: input timed out", incount);
844             }
845             wgetch_wrap(win, first_y);
846         }
847         if (c == ERR && blocking_getch(flags, delay)) {
848             wprintw(win, "ERR");
849             wgetch_wrap(win, first_y);
850         } else if (isQuit(c, ExitOnEscape())) {
851             break;
852         } else if (c == 'e') {
853             flags[UChar('e')] = !flags[UChar('e')];
854             setup_getch(win, flags);
855             wgetch_help(win, flags);
856         } else if (c == 'g') {
857             waddstr(win, "getstr test: ");
858             echo();
859             c = wgetnstr(win, buf, sizeof(buf) - 1);
860             noecho();
861             wprintw(win, "I saw %d characters:\n\t`%s' (%s).",
862                     (int) strlen(buf), buf,
863                     ok_keyname(c));
864             wclrtoeol(win);
865             wgetch_wrap(win, first_y);
866         } else if (c == 'k') {
867             flags[UChar('k')] = !flags[UChar('k')];
868             setup_getch(win, flags);
869             wgetch_help(win, flags);
870         } else if (c == 'm') {
871             flags[UChar('m')] = !flags[UChar('m')];
872             setup_getch(win, flags);
873             wgetch_help(win, flags);
874         } else if (c == 's') {
875             ShellOut(TRUE);
876         } else if (c == 't') {
877             notimeout(win, flags[UChar('t')]);
878             flags[UChar('t')] = !flags[UChar('t')];
879             wgetch_help(win, flags);
880         } else if (c == 'w') {
881             int high = getmaxy(win) - 1 - first_y + 1;
882             int wide = getmaxx(win) - first_x;
883             int old_y, old_x;
884             int new_y = first_y + getbegy(win);
885             int new_x = first_x + getbegx(win);
886
887             getyx(win, old_y, old_x);
888             if (high > 2 && wide > 2) {
889                 WINDOW *wb = newwin(high, wide, new_y, new_x);
890                 WINDOW *wi = newwin(high - 2, wide - 2, new_y + 1, new_x + 1);
891
892                 box(wb, 0, 0);
893                 wrefresh(wb);
894                 wmove(wi, 0, 0);
895                 remember_boxes(level, wi, wb);
896                 wgetch_test(level + 1, wi, delay);
897                 delwin(wi);
898                 delwin(wb);
899
900                 wgetch_help(win, flags);
901                 wmove(win, old_y, old_x);
902                 touchwin(win);
903                 wrefresh(win);
904                 doupdate();
905             }
906 #ifdef SIGTSTP
907         } else if (c == 'z') {
908             kill(getpid(), SIGTSTP);
909 #endif
910         } else {
911             wprintw(win, "Key pressed: %04o ", c);
912 #ifdef NCURSES_MOUSE_VERSION
913             if (c == KEY_MOUSE) {
914                 show_mouse(win);
915             } else
916 #endif /* NCURSES_MOUSE_VERSION */
917             if (c >= KEY_MIN) {
918 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
919                 if (c == KEY_RESIZE) {
920                     resize_boxes(level, win);
921                 }
922 #endif
923                 (void) waddstr(win, keyname(c));
924             } else if (c >= 0x80) {
925                 unsigned c2 = (unsigned) c;
926 #if !(defined(NCURSES_VERSION) || defined(_XOPEN_CURSES))
927                 /* at least Solaris SVR4 curses breaks unctrl(128), etc. */
928                 c2 &= 0x7f;
929 #endif
930                 if (isprint(c))
931                     (void) wprintw(win, "%c", UChar(c));
932                 else if (c2 != UChar(c))
933                     (void) wprintw(win, "M-%s", unctrl(c2));
934                 else
935                     (void) wprintw(win, "%s", unctrl(c2));
936                 waddstr(win, " (high-half character)");
937             } else {
938                 if (isprint(c))
939                     (void) wprintw(win, "%c (ASCII printable character)", c);
940                 else
941                     (void) wprintw(win, "%s (ASCII control character)",
942                                    unctrl(UChar(c)));
943             }
944             wgetch_wrap(win, first_y);
945         }
946     }
947
948     wtimeout(win, -1);
949
950     if (!level)
951         init_getch(win, flags, delay);
952 }
953
954 static int
955 begin_getch_test(void)
956 {
957     char buf[BUFSIZ];
958     int delay;
959
960     refresh();
961
962 #ifdef NCURSES_MOUSE_VERSION
963     mousemask(ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION, (mmask_t *) 0);
964 #endif
965
966     (void) printw("Delay in 10ths of a second (<CR> for blocking input)? ");
967     echo();
968     getnstr(buf, sizeof(buf) - 1);
969     noecho();
970     nonl();
971
972     if (isdigit(UChar(buf[0]))) {
973         delay = atoi(buf) * 100;
974     } else {
975         delay = -1;
976     }
977     raw();
978     move(6, 0);
979     return delay;
980 }
981
982 static void
983 finish_getch_test(void)
984 {
985 #ifdef NCURSES_MOUSE_VERSION
986     mousemask(0, (mmask_t *) 0);
987 #endif
988     erase();
989     noraw();
990     nl();
991     endwin();
992 }
993
994 static int
995 getch_test(bool recur GCC_UNUSED)
996 {
997     int delay = begin_getch_test();
998
999     slk_restore();
1000     wgetch_test(0, stdscr, delay);
1001     forget_boxes();
1002     finish_getch_test();
1003     slk_clear();
1004     return OK;
1005 }
1006
1007 #if USE_WIDEC_SUPPORT
1008 /*
1009  * For wget_wch_test(), we create pairs of windows - one for a box, one for text.
1010  * Resize both and paint the box in the parent.
1011  */
1012 #if defined(KEY_RESIZE) && HAVE_WRESIZE
1013 static void
1014 resize_wide_boxes(unsigned level, WINDOW *win)
1015 {
1016     unsigned n;
1017     int base = 5;
1018     int high = LINES - base;
1019     int wide = COLS;
1020
1021     touchwin(stdscr);
1022     wnoutrefresh(stdscr);
1023
1024     slk_repaint();
1025
1026     for (n = 0; n < level; ++n) {
1027         wresize(winstack[n].frame, high, wide);
1028         wresize(winstack[n].text, high - 2, wide - 2);
1029         high -= 2;
1030         wide -= 2;
1031         werase(winstack[n].text);
1032         box_set(winstack[n].frame, 0, 0);
1033         wnoutrefresh(winstack[n].frame);
1034         wprintw(winstack[n].text,
1035                 "size %dx%d\n",
1036                 getmaxy(winstack[n].text),
1037                 getmaxx(winstack[n].text));
1038         wnoutrefresh(winstack[n].text);
1039         if (winstack[n].text == win)
1040             break;
1041     }
1042     doupdate();
1043 }
1044 #endif /* KEY_RESIZE */
1045
1046 static char *
1047 wcstos(const wchar_t *src)
1048 {
1049     int need;
1050     char *result = 0;
1051     const wchar_t *tmp = src;
1052 #ifndef state_unused
1053     mbstate_t state;
1054 #endif
1055
1056     reset_wchars(state);
1057     if ((need = (int) count_wchars(tmp, 0, &state)) > 0) {
1058         unsigned have = (unsigned) need;
1059         if ((result = typeCalloc(char, have + 1)) != 0) {
1060             tmp = src;
1061             if (trans_wchars(result, tmp, have, &state) != have) {
1062                 free(result);
1063                 result = 0;
1064             }
1065         } else {
1066             failed("wcstos");
1067         }
1068     }
1069     return result;
1070 }
1071
1072 static void
1073 wget_wch_test(unsigned level, WINDOW *win, int delay)
1074 {
1075     wchar_t wchar_buf[BUFSIZ];
1076     wint_t wint_buf[BUFSIZ];
1077     int first_y, first_x;
1078     wint_t c;
1079     int incount = 0;
1080     GetchFlags flags;
1081     char *temp;
1082
1083     init_getch(win, flags, delay);
1084     notimeout(win, FALSE);
1085     wtimeout(win, delay);
1086     getyx(win, first_y, first_x);
1087
1088     wgetch_help(win, flags);
1089     wsetscrreg(win, first_y, getmaxy(win) - 1);
1090     scrollok(win, TRUE);
1091
1092     for (;;) {
1093         int code;
1094
1095         while ((code = wGet_wchar(win, &c)) == ERR) {
1096             incount++;
1097             if (blocking_getch(flags, delay)) {
1098                 (void) wprintw(win, "%05d: input error", incount);
1099                 break;
1100             } else {
1101                 (void) wprintw(win, "%05d: input timed out", incount);
1102             }
1103             wgetch_wrap(win, first_y);
1104         }
1105         if (code == ERR && blocking_getch(flags, delay)) {
1106             wprintw(win, "ERR");
1107             wgetch_wrap(win, first_y);
1108         } else if (isQuit((int) c, ExitOnEscape())) {
1109             break;
1110         } else if (c == 'e') {
1111             flags[UChar('e')] = !flags[UChar('e')];
1112             setup_getch(win, flags);
1113             wgetch_help(win, flags);
1114         } else if (c == 'g') {
1115             waddstr(win, "getstr test: ");
1116             echo();
1117             code = wgetn_wstr(win, wint_buf, BUFSIZ - 1);
1118             noecho();
1119             if (code == ERR) {
1120                 wprintw(win, "wgetn_wstr returns an error.");
1121             } else {
1122                 int n;
1123                 for (n = 0; (wchar_buf[n] = (wchar_t) wint_buf[n]) != 0; ++n) {
1124                     ;
1125                 }
1126                 if ((temp = wcstos(wchar_buf)) != 0) {
1127                     wprintw(win, "I saw %d characters:\n\t`%s'.",
1128                             (int) wcslen(wchar_buf), temp);
1129                     free(temp);
1130                 } else {
1131                     wprintw(win, "I saw %d characters (cannot convert).",
1132                             (int) wcslen(wchar_buf));
1133                 }
1134             }
1135             wclrtoeol(win);
1136             wgetch_wrap(win, first_y);
1137         } else if (c == 'k') {
1138             flags[UChar('k')] = !flags[UChar('k')];
1139             setup_getch(win, flags);
1140             wgetch_help(win, flags);
1141         } else if (c == 'm') {
1142             flags[UChar('m')] = !flags[UChar('m')];
1143             setup_getch(win, flags);
1144             wgetch_help(win, flags);
1145         } else if (c == 's') {
1146             ShellOut(TRUE);
1147         } else if (c == 't') {
1148             notimeout(win, flags[UChar('t')]);
1149             flags[UChar('t')] = !flags[UChar('t')];
1150             wgetch_help(win, flags);
1151         } else if (c == 'w') {
1152             int high = getmaxy(win) - 1 - first_y + 1;
1153             int wide = getmaxx(win) - first_x;
1154             int old_y, old_x;
1155             int new_y = first_y + getbegy(win);
1156             int new_x = first_x + getbegx(win);
1157
1158             getyx(win, old_y, old_x);
1159             if (high > 2 && wide > 2) {
1160                 WINDOW *wb = newwin(high, wide, new_y, new_x);
1161                 WINDOW *wi = newwin(high - 2, wide - 2, new_y + 1, new_x + 1);
1162
1163                 box_set(wb, 0, 0);
1164                 wrefresh(wb);
1165                 wmove(wi, 0, 0);
1166                 remember_boxes(level, wi, wb);
1167                 wget_wch_test(level + 1, wi, delay);
1168                 delwin(wi);
1169                 delwin(wb);
1170
1171                 wgetch_help(win, flags);
1172                 wmove(win, old_y, old_x);
1173                 touchwin(win);
1174                 wrefresh(win);
1175             }
1176 #ifdef SIGTSTP
1177         } else if (c == 'z') {
1178             kill(getpid(), SIGTSTP);
1179 #endif
1180         } else {
1181             wprintw(win, "Key pressed: %04o ", (int) c);
1182 #ifdef NCURSES_MOUSE_VERSION
1183             if (c == KEY_MOUSE) {
1184                 show_mouse(win);
1185             } else
1186 #endif /* NCURSES_MOUSE_VERSION */
1187             if (code == KEY_CODE_YES) {
1188 #if defined(KEY_RESIZE) && HAVE_WRESIZE
1189                 if (c == KEY_RESIZE) {
1190                     resize_wide_boxes(level, win);
1191                 }
1192 #endif
1193                 (void) waddstr(win, keyname((wchar_t) c));
1194             } else {
1195                 (void) waddstr(win, key_name((wchar_t) c));
1196                 if (c < 256 && iscntrl(c)) {
1197                     (void) wprintw(win, " (control character)");
1198                 } else {
1199                     (void) wprintw(win, " = %#x (printable character)",
1200                                    (unsigned) c);
1201                 }
1202             }
1203             wgetch_wrap(win, first_y);
1204         }
1205     }
1206
1207     wtimeout(win, -1);
1208
1209     if (!level)
1210         init_getch(win, flags, delay);
1211 }
1212
1213 static int
1214 x_getch_test(bool recur GCC_UNUSED)
1215 {
1216     int delay = begin_getch_test();
1217
1218     slk_restore();
1219     wget_wch_test(0, stdscr, delay);
1220     forget_boxes();
1221     finish_getch_test();
1222     slk_clear();
1223     return OK;
1224 }
1225 #endif
1226
1227 /****************************************************************************
1228  *
1229  * Character attributes test
1230  *
1231  ****************************************************************************/
1232
1233 #if HAVE_SETUPTERM || HAVE_TGETENT
1234 #define get_ncv() TIGETNUM("ncv","NC")
1235 #define get_xmc() TIGETNUM("xmc","sg")
1236 #else
1237 #define get_ncv() -1
1238 #define get_xmc() -1
1239 #endif
1240
1241 #if !HAVE_TERMATTRS
1242 static chtype
1243 my_termattrs(void)
1244 {
1245     static int first = TRUE;
1246     static chtype result = 0;
1247
1248     if (first) {
1249 #if !HAVE_TIGETSTR
1250         char buffer[4096];
1251         char parsed[4096];
1252         char *area_pointer = parsed;
1253
1254         tgetent(buffer, getenv("TERM"));
1255 #endif
1256
1257         if (TIGETSTR("smso", "so"))
1258             result |= A_STANDOUT;
1259         if (TIGETSTR("smul", "us"))
1260             result |= A_UNDERLINE;
1261         if (TIGETSTR("rev", "mr"))
1262             result |= A_REVERSE;
1263         if (TIGETSTR("blink", "mb"))
1264             result |= A_BLINK;
1265         if (TIGETSTR("dim", "mh"))
1266             result |= A_DIM;
1267         if (TIGETSTR("bold", "md"))
1268             result |= A_BOLD;
1269         if (TIGETSTR("smacs", "ac"))
1270             result |= A_ALTCHARSET;
1271
1272         first = FALSE;
1273     }
1274     return result;
1275 }
1276 #define termattrs() my_termattrs()
1277 #endif
1278
1279 #define ATTRSTRING_1ST 32       /* ' ' */
1280 #define ATTRSTRING_END 126      /* '~' */
1281
1282 #define COLS_PRE_ATTRS 5
1283 #define COLS_AFT_ATTRS 15
1284 #define COL_ATTRSTRING (COLS_PRE_ATTRS + 17)
1285 #define LEN_ATTRSTRING (COLS - (COL_ATTRSTRING + COLS_AFT_ATTRS))
1286 #define MAX_ATTRSTRING (ATTRSTRING_END + 1 - ATTRSTRING_1ST)
1287
1288 static char attr_test_string[MAX_ATTRSTRING + 1];
1289
1290 static void
1291 attr_legend(WINDOW *helpwin)
1292 {
1293     int row = 1;
1294     int col = 1;
1295
1296     MvWPrintw(helpwin, row++, col,
1297               "ESC to exit.");
1298     MvWPrintw(helpwin, row++, col,
1299               "^L repaints.");
1300     ++row;
1301     MvWPrintw(helpwin, row++, col,
1302               "Modify the test strings:");
1303     MvWPrintw(helpwin, row++, col,
1304               "  A digit sets gaps on each side of displayed attributes");
1305     MvWPrintw(helpwin, row++, col,
1306               "  </> shifts the text left/right. ");
1307     ++row;
1308     MvWPrintw(helpwin, row++, col,
1309               "Toggles:");
1310     if (UseColors) {
1311         MvWPrintw(helpwin, row++, col,
1312                   "  f/F/b/B toggle foreground/background background color");
1313         MvWPrintw(helpwin, row++, col,
1314                   "  t/T     toggle text/background color attribute");
1315     }
1316     MvWPrintw(helpwin, row++, col,
1317               "  a/A     toggle ACS (alternate character set) mapping");
1318     MvWPrintw(helpwin, row, col,
1319               "  v/V     toggle video attribute to combine with each line");
1320 #if USE_WIDEC_SUPPORT
1321     MvWPrintw(helpwin, row, col,
1322               "  w/W     toggle normal/wide (double-width) test-characters");
1323 #endif
1324 }
1325
1326 static void
1327 show_color_attr(int fg, int bg, int tx)
1328 {
1329     if (UseColors) {
1330         printw("  Colors (fg %d, bg %d", fg, bg);
1331         if (tx >= 0)
1332             printw(", text %d", tx);
1333         printw("),");
1334     }
1335 }
1336
1337 static bool
1338 cycle_color_attr(int ch, NCURSES_COLOR_T *fg, NCURSES_COLOR_T *bg, NCURSES_COLOR_T *tx)
1339 {
1340     bool error = FALSE;
1341
1342     if (UseColors) {
1343         switch (ch) {
1344         case 'f':
1345             *fg = (NCURSES_COLOR_T) (*fg + 1);
1346             break;
1347         case 'F':
1348             *fg = (NCURSES_COLOR_T) (*fg - 1);
1349             break;
1350         case 'b':
1351             *bg = (NCURSES_COLOR_T) (*bg + 1);
1352             break;
1353         case 'B':
1354             *bg = (NCURSES_COLOR_T) (*bg - 1);
1355             break;
1356         case 't':
1357             *tx = (NCURSES_COLOR_T) (*tx + 1);
1358             break;
1359         case 'T':
1360             *tx = (NCURSES_COLOR_T) (*tx - 1);
1361             break;
1362         default:
1363             beep();
1364             error = TRUE;
1365             break;
1366         }
1367         if (*fg >= COLORS)
1368             *fg = (NCURSES_COLOR_T) MinColors;
1369         if (*fg < MinColors)
1370             *fg = (NCURSES_COLOR_T) (COLORS - 1);
1371         if (*bg >= COLORS)
1372             *bg = (NCURSES_COLOR_T) MinColors;
1373         if (*bg < MinColors)
1374             *bg = (NCURSES_COLOR_T) (COLORS - 1);
1375         if (*tx >= COLORS)
1376             *tx = -1;
1377         if (*tx < -1)
1378             *tx = (NCURSES_COLOR_T) (COLORS - 1);
1379     } else {
1380         beep();
1381         error = TRUE;
1382     }
1383     return error;
1384 }
1385
1386 static void
1387 adjust_attr_string(int adjust)
1388 {
1389     char save = attr_test_string[0];
1390     int first = ((int) UChar(save)) + adjust;
1391
1392     if (first >= ATTRSTRING_1ST) {
1393         int j, k;
1394
1395         for (j = 0, k = first; j < MAX_ATTRSTRING; ++j, ++k) {
1396             if (k > ATTRSTRING_END)
1397                 break;
1398             attr_test_string[j] = (char) k;
1399             if (((k + 1 - first) % 5) == 0) {
1400                 if (++j >= MAX_ATTRSTRING)
1401                     break;
1402                 attr_test_string[j] = ' ';
1403             }
1404         }
1405         if ((LEN_ATTRSTRING - j) > 5) {
1406             attr_test_string[0] = save;
1407             adjust_attr_string(adjust - 1);
1408         } else {
1409             while (j < MAX_ATTRSTRING)
1410                 attr_test_string[j++] = ' ';
1411             attr_test_string[j] = '\0';
1412         }
1413     }
1414 }
1415
1416 /*
1417  * Prefer the right-end of the string for starting, since that maps to the
1418  * VT100 line-drawing.
1419  */
1420 static int
1421 default_attr_string(void)
1422 {
1423     int result = (ATTRSTRING_END - LEN_ATTRSTRING);
1424     result += (LEN_ATTRSTRING / 5);
1425     if (result < ATTRSTRING_1ST)
1426         result = ATTRSTRING_1ST;
1427     return result;
1428 }
1429
1430 static void
1431 init_attr_string(void)
1432 {
1433     attr_test_string[0] = (char) default_attr_string();
1434     adjust_attr_string(0);
1435 }
1436
1437 static int
1438 show_attr(WINDOW *win, int row, int skip, bool arrow, chtype attr, const char *name)
1439 {
1440     int ncv = get_ncv();
1441     chtype test = attr & (chtype) (~(A_ALTCHARSET | A_CHARTEXT));
1442
1443     if (arrow)
1444         MvPrintw(row, COLS_PRE_ATTRS - 3, "-->");
1445     MvPrintw(row, COLS_PRE_ATTRS, "%s mode:", name);
1446     MvPrintw(row, COL_ATTRSTRING - 1, "|");
1447     if (skip)
1448         printw("%*s", skip, " ");
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     wmove(win, 0, 0);
1455     werase(win);
1456     if (attr & A_ALTCHARSET) {
1457         const char *s;
1458
1459         for (s = attr_test_string; *s != '\0'; ++s) {
1460             chtype ch = UChar(*s);
1461             (void) waddch(win, ch | attr);
1462         }
1463     } else {
1464         (void) wattrset(win, AttrArg(attr, 0));
1465         (void) waddstr(win, attr_test_string);
1466         (void) wattroff(win, (int) attr);
1467     }
1468     if (skip)
1469         printw("%*s", skip, " ");
1470     MvPrintw(row, COL_ATTRSTRING + LEN_ATTRSTRING, "|");
1471     if (test != A_NORMAL) {
1472         if (!(termattrs() & test)) {
1473             printw(" (N/A)");
1474         } else {
1475             if (ncv > 0 && stdscr && (getbkgd(stdscr) & A_COLOR)) {
1476                 static const chtype table[] =
1477                 {
1478                     A_STANDOUT,
1479                     A_UNDERLINE,
1480                     A_REVERSE,
1481                     A_BLINK,
1482                     A_DIM,
1483                     A_BOLD,
1484 #ifdef A_INVIS
1485                     A_INVIS,
1486 #endif
1487 #ifdef A_ITALIC
1488                     A_ITALIC,
1489 #endif
1490                     A_PROTECT,
1491                     A_ALTCHARSET
1492                 };
1493                 unsigned n;
1494                 bool found = FALSE;
1495                 for (n = 0; n < SIZEOF(table); n++) {
1496                     if ((table[n] & attr) != 0
1497                         && ((1 << n) & ncv) != 0) {
1498                         found = TRUE;
1499                         break;
1500                     }
1501                 }
1502                 if (found)
1503                     printw(" (NCV)");
1504             }
1505             if ((termattrs() & test) != test) {
1506                 printw(" (Part)");
1507             }
1508         }
1509     }
1510     return row + 2;
1511 }
1512
1513 typedef struct {
1514     chtype attr;
1515     NCURSES_CONST char *name;
1516 } ATTR_TBL;
1517 /* *INDENT-OFF* */
1518 static const ATTR_TBL attrs_to_test[] = {
1519     { A_STANDOUT,       "STANDOUT" },
1520     { A_REVERSE,        "REVERSE" },
1521     { A_BOLD,           "BOLD" },
1522     { A_UNDERLINE,      "UNDERLINE" },
1523     { A_DIM,            "DIM" },
1524     { A_BLINK,          "BLINK" },
1525     { A_PROTECT,        "PROTECT" },
1526 #ifdef A_INVIS
1527     { A_INVIS,          "INVISIBLE" },
1528 #endif
1529 #ifdef A_ITALIC
1530     { A_ITALIC,         "ITALIC" },
1531 #endif
1532     { A_NORMAL,         "NORMAL" },
1533 };
1534 /* *INDENT-ON* */
1535
1536 static unsigned
1537 init_attr_list(ATTR_TBL * target, attr_t attrs)
1538 {
1539     unsigned result = 0;
1540     size_t n;
1541
1542     for (n = 0; n < SIZEOF(attrs_to_test); ++n) {
1543         attr_t test = attrs_to_test[n].attr;
1544         if (test == A_NORMAL || (test & attrs) != 0) {
1545             target[result++] = attrs_to_test[n];
1546         }
1547     }
1548     return result;
1549 }
1550
1551 #if USE_WIDEC_SUPPORT
1552 typedef struct {
1553     attr_t attr;
1554     NCURSES_CONST char *name;
1555 } W_ATTR_TBL;
1556 /* *INDENT-OFF* */
1557 static const W_ATTR_TBL w_attrs_to_test[] = {
1558     { WA_STANDOUT,      "STANDOUT" },
1559     { WA_REVERSE,       "REVERSE" },
1560     { WA_BOLD,          "BOLD" },
1561     { WA_UNDERLINE,     "UNDERLINE" },
1562     { WA_DIM,           "DIM" },
1563     { WA_BLINK,         "BLINK" },
1564     { WA_PROTECT,       "PROTECT" },
1565 #ifdef WA_INVIS
1566     { WA_INVIS,         "INVISIBLE" },
1567 #endif
1568 #ifdef WA_ITALIC
1569     { WA_ITALIC,        "ITALIC" },
1570 #endif
1571     { WA_NORMAL,        "NORMAL" },
1572 };
1573 /* *INDENT-ON* */
1574
1575 static unsigned
1576 init_w_attr_list(W_ATTR_TBL * target, attr_t attrs)
1577 {
1578     unsigned result = 0;
1579     size_t n;
1580
1581     for (n = 0; n < SIZEOF(w_attrs_to_test); ++n) {
1582         attr_t test = w_attrs_to_test[n].attr;
1583         if (test == WA_NORMAL || (test & attrs) != 0) {
1584             target[result++] = w_attrs_to_test[n];
1585         }
1586     }
1587     return result;
1588 }
1589 #endif
1590
1591 static bool
1592 attr_getc(int *skip,
1593           NCURSES_COLOR_T *fg,
1594           NCURSES_COLOR_T *bg,
1595           NCURSES_COLOR_T *tx,
1596           int *ac,
1597           unsigned *kc,
1598           unsigned limit)
1599 {
1600     bool result = TRUE;
1601     bool error = FALSE;
1602     WINDOW *helpwin;
1603
1604     do {
1605         int ch = Getchar();
1606
1607         error = FALSE;
1608         if (ch < 256 && isdigit(ch)) {
1609             *skip = (ch - '0');
1610         } else {
1611             switch (ch) {
1612             case CTRL('L'):
1613                 Repaint();
1614                 break;
1615             case HELP_KEY_1:
1616                 if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
1617                     box(helpwin, 0, 0);
1618                     attr_legend(helpwin);
1619                     wGetchar(helpwin);
1620                     delwin(helpwin);
1621                 }
1622                 break;
1623             case 'a':
1624                 *ac = 0;
1625                 break;
1626             case 'A':
1627                 *ac = A_ALTCHARSET;
1628                 break;
1629             case 'v':
1630                 if (*kc == 0)
1631                     *kc = limit - 1;
1632                 else
1633                     *kc -= 1;
1634                 break;
1635             case 'V':
1636                 *kc += 1;
1637                 if (*kc >= limit)
1638                     *kc = 0;
1639                 break;
1640             case '<':
1641                 adjust_attr_string(-1);
1642                 break;
1643             case '>':
1644                 adjust_attr_string(1);
1645                 break;
1646             case case_QUIT:
1647                 result = FALSE;
1648                 break;
1649             default:
1650                 error = cycle_color_attr(ch, fg, bg, tx);
1651                 break;
1652             }
1653         }
1654     } while (error);
1655     return result;
1656 }
1657
1658 static int
1659 attr_test(bool recur GCC_UNUSED)
1660 /* test text attributes */
1661 {
1662     int n;
1663     int skip = get_xmc();
1664     NCURSES_COLOR_T fg = COLOR_BLACK;   /* color pair 0 is special */
1665     NCURSES_COLOR_T bg = COLOR_BLACK;
1666     NCURSES_COLOR_T tx = -1;
1667     int ac = 0;
1668     WINDOW *my_wins[SIZEOF(attrs_to_test)];
1669     ATTR_TBL my_list[SIZEOF(attrs_to_test)];
1670     unsigned my_size = init_attr_list(my_list, termattrs());
1671
1672     if (my_size > 1) {
1673         unsigned j, k;
1674
1675         for (j = 0; j < my_size; ++j) {
1676             my_wins[j] = subwin(stdscr,
1677                                 1, LEN_ATTRSTRING,
1678                                 2 + (int) (2 * j), COL_ATTRSTRING);
1679             scrollok(my_wins[j], FALSE);
1680         }
1681
1682         if (skip < 0)
1683             skip = 0;
1684
1685         n = skip;               /* make it easy */
1686         k = my_size - 1;
1687         init_attr_string();
1688
1689         do {
1690             int row = 2;
1691             chtype normal = A_NORMAL | BLANK;
1692             chtype extras = (chtype) ac;
1693
1694             if (UseColors) {
1695                 NCURSES_PAIRS_T pair = 0;
1696                 if ((fg != COLOR_BLACK) || (bg != COLOR_BLACK)) {
1697                     pair = 1;
1698                     if (init_pair(pair, fg, bg) == ERR) {
1699                         beep();
1700                     } else {
1701                         normal |= (chtype) COLOR_PAIR(pair);
1702                     }
1703                 }
1704                 if (tx >= 0) {
1705                     pair = 2;
1706                     if (init_pair(pair, tx, bg) == ERR) {
1707                         beep();
1708                     } else {
1709                         extras |= (chtype) COLOR_PAIR(pair);
1710                         normal &= ~A_COLOR;
1711                     }
1712                 }
1713             }
1714             bkgd(normal);
1715             bkgdset(normal);
1716             erase();
1717
1718             box(stdscr, 0, 0);
1719             MvAddStr(0, 20, "Character attribute test display");
1720
1721             for (j = 0; j < my_size; ++j) {
1722                 bool arrow = (j == k);
1723                 row = show_attr(my_wins[j], row, n, arrow,
1724                                 normal |
1725                                 extras |
1726                                 my_list[j].attr |
1727                                 my_list[k].attr,
1728                                 my_list[j].name);
1729             }
1730
1731             MvPrintw(row, COLS_PRE_ATTRS,
1732                      "This terminal does %shave the magic-cookie glitch",
1733                      get_xmc() > -1 ? "" : "not ");
1734             MvPrintw(row + 1, COLS_PRE_ATTRS, "Enter '?' for help.");
1735             show_color_attr(fg, bg, tx);
1736             printw("  ACS (%d)", ac != 0);
1737
1738             refresh();
1739         } while (attr_getc(&n, &fg, &bg, &tx, &ac, &k, my_size));
1740
1741         bkgdset(A_NORMAL | BLANK);
1742         erase();
1743         endwin();
1744         return OK;
1745     } else {
1746         Cannot("does not support video attributes.");
1747         return ERR;
1748     }
1749 }
1750
1751 #if USE_WIDEC_SUPPORT
1752 static bool use_fullwidth;
1753 static wchar_t wide_attr_test_string[MAX_ATTRSTRING + 1];
1754
1755 #define FULL_LO 0xff00
1756 #define FULL_HI 0xff5e
1757 #define HALF_LO 0x20
1758
1759 #define isFullWidth(ch)   ((int)(ch) >= FULL_LO && (int)(ch) <= FULL_HI)
1760 #define ToNormalWidth(ch) (wchar_t) (((int)(ch) - FULL_LO) + HALF_LO)
1761 #define ToFullWidth(ch)   (wchar_t) (((int)(ch) - HALF_LO) + FULL_LO)
1762
1763 /*
1764  * Returns an ASCII code in [32..126]
1765  */
1766 static wchar_t
1767 normal_wchar(int ch)
1768 {
1769     wchar_t result = (wchar_t) ch;
1770     if (isFullWidth(ch))
1771         result = ToNormalWidth(ch);
1772     return result;
1773 }
1774
1775 /*
1776  * Returns either an ASCII code in in [32..126] or full-width in
1777  * [0xff00..0xff5e], according to use_fullwidth setting.
1778  */
1779 static wchar_t
1780 target_wchar(int ch)
1781 {
1782     wchar_t result = (wchar_t) ch;
1783     if (use_fullwidth) {
1784         if (!isFullWidth(ch))
1785             result = ToFullWidth(ch);
1786     } else {
1787         if (isFullWidth(ch))
1788             result = ToNormalWidth(ch);
1789     }
1790     return result;
1791 }
1792
1793 static void
1794 wide_adjust_attr_string(int adjust)
1795 {
1796     wchar_t save = wide_attr_test_string[0];
1797     int first = ((int) normal_wchar(save)) + adjust;
1798
1799     if (first >= ATTRSTRING_1ST) {
1800         int j, k;
1801
1802         for (j = 0, k = first; j < MAX_ATTRSTRING; ++j, ++k) {
1803             if (k > ATTRSTRING_END)
1804                 break;
1805             wide_attr_test_string[j] = target_wchar(k);
1806             if (((k + 1 - first) % 5) == 0) {
1807                 if (++j >= MAX_ATTRSTRING)
1808                     break;
1809                 wide_attr_test_string[j] = ' ';
1810             }
1811         }
1812         if ((LEN_ATTRSTRING - j) > 5) {
1813             wide_attr_test_string[0] = save;
1814             wide_adjust_attr_string(adjust - 1);
1815         } else {
1816             while (j < MAX_ATTRSTRING)
1817                 wide_attr_test_string[j++] = ' ';
1818             wide_attr_test_string[j] = '\0';
1819         }
1820     }
1821 }
1822
1823 static void
1824 wide_init_attr_string(void)
1825 {
1826     use_fullwidth = FALSE;
1827     wide_attr_test_string[0] = (wchar_t) default_attr_string();
1828     wide_adjust_attr_string(0);
1829 }
1830
1831 static void
1832 set_wide_background(NCURSES_PAIRS_T pair)
1833 {
1834     cchar_t normal;
1835     wchar_t blank[2];
1836
1837     blank[0] = ' ';
1838     blank[1] = 0;
1839     setcchar(&normal, blank, A_NORMAL, pair, 0);
1840     bkgrnd(&normal);
1841     bkgrndset(&normal);
1842 }
1843
1844 static attr_t
1845 get_wide_background(void)
1846 {
1847     attr_t result = WA_NORMAL;
1848     attr_t attr;
1849     cchar_t ch;
1850     NCURSES_PAIRS_T pair;
1851
1852     memset(&ch, 0, sizeof(ch));
1853     if (getbkgrnd(&ch) != ERR) {
1854         wchar_t wch[CCHARW_MAX];
1855
1856         if (getcchar(&ch, wch, &attr, &pair, 0) != ERR) {
1857             result = attr;
1858         }
1859     }
1860     return result;
1861 }
1862
1863 static int
1864 wide_show_attr(WINDOW *win,
1865                int row,
1866                int skip,
1867                bool arrow,
1868                attr_t attr,
1869                NCURSES_PAIRS_T pair,
1870                const char *name)
1871 {
1872     int ncv = get_ncv();
1873     attr_t test = attr & ~WA_ALTCHARSET;
1874
1875     if (arrow)
1876         MvPrintw(row, COLS_PRE_ATTRS - 3, "-->");
1877     MvPrintw(row, COLS_PRE_ATTRS, "%s mode:", name);
1878     MvPrintw(row, COL_ATTRSTRING - 1, "|");
1879     if (skip)
1880         printw("%*s", skip, " ");
1881
1882     /*
1883      * Just for testing, write text using the alternate character set one
1884      * character at a time (to pass its rendition directly), and use the
1885      * string operation for the other attributes.
1886      */
1887     wmove(win, 0, 0);
1888     werase(win);
1889     if (attr & WA_ALTCHARSET) {
1890         const wchar_t *s;
1891         cchar_t ch;
1892
1893         for (s = wide_attr_test_string; *s != L'\0'; ++s) {
1894             wchar_t fill[2];
1895             fill[0] = *s;
1896             fill[1] = L'\0';
1897             setcchar(&ch, fill, attr, pair, 0);
1898             (void) wadd_wch(win, &ch);
1899         }
1900     } else {
1901         attr_t old_attr = 0;
1902         NCURSES_PAIRS_T old_pair = 0;
1903
1904         (void) (wattr_get) (win, &old_attr, &old_pair, 0);
1905         (void) wattr_set(win, attr, pair, 0);
1906         (void) waddwstr(win, wide_attr_test_string);
1907         (void) wattr_set(win, old_attr, old_pair, 0);
1908     }
1909     if (skip)
1910         printw("%*s", skip, " ");
1911     MvPrintw(row, COL_ATTRSTRING + LEN_ATTRSTRING, "|");
1912     if (test != A_NORMAL) {
1913         if (!(term_attrs() & test)) {
1914             printw(" (N/A)");
1915         } else {
1916             if (ncv > 0 && (get_wide_background() & A_COLOR)) {
1917                 static const attr_t table[] =
1918                 {
1919                     WA_STANDOUT,
1920                     WA_UNDERLINE,
1921                     WA_REVERSE,
1922                     WA_BLINK,
1923                     WA_DIM,
1924                     WA_BOLD,
1925                     WA_INVIS,
1926                     WA_PROTECT,
1927                     WA_ALTCHARSET
1928                 };
1929                 unsigned n;
1930                 bool found = FALSE;
1931                 for (n = 0; n < SIZEOF(table); n++) {
1932                     if ((table[n] & attr) != 0
1933                         && ((1 << n) & ncv) != 0) {
1934                         found = TRUE;
1935                         break;
1936                     }
1937                 }
1938                 if (found)
1939                     printw(" (NCV)");
1940             }
1941             if ((term_attrs() & test) != test) {
1942                 printw(" (Part)");
1943             }
1944         }
1945     }
1946     return row + 2;
1947 }
1948
1949 static bool
1950 wide_attr_getc(int *skip,
1951                NCURSES_COLOR_T *fg, NCURSES_COLOR_T *bg,
1952                NCURSES_COLOR_T *tx, int *ac,
1953                unsigned *kc, unsigned limit)
1954 {
1955     bool result = TRUE;
1956     bool error = FALSE;
1957     WINDOW *helpwin;
1958
1959     do {
1960         int ch = Getchar();
1961
1962         error = FALSE;
1963         if (ch < 256 && isdigit(ch)) {
1964             *skip = (ch - '0');
1965         } else {
1966             switch (ch) {
1967             case CTRL('L'):
1968                 Repaint();
1969                 break;
1970             case HELP_KEY_1:
1971                 if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
1972                     box_set(helpwin, 0, 0);
1973                     attr_legend(helpwin);
1974                     wGetchar(helpwin);
1975                     delwin(helpwin);
1976                 }
1977                 break;
1978             case 'a':
1979                 *ac = 0;
1980                 break;
1981             case 'A':
1982                 *ac = A_ALTCHARSET;
1983                 break;
1984             case 'v':
1985                 if (*kc == 0)
1986                     *kc = limit - 1;
1987                 else
1988                     *kc -= 1;
1989                 break;
1990             case 'V':
1991                 *kc += 1;
1992                 if (*kc >= limit)
1993                     *kc = 0;
1994                 break;
1995             case 'w':
1996                 use_fullwidth = FALSE;
1997                 wide_adjust_attr_string(0);
1998                 break;
1999             case 'W':
2000                 use_fullwidth = TRUE;
2001                 wide_adjust_attr_string(0);
2002                 break;
2003             case '<':
2004                 wide_adjust_attr_string(-1);
2005                 break;
2006             case '>':
2007                 wide_adjust_attr_string(1);
2008                 break;
2009             case case_QUIT:
2010                 result = FALSE;
2011                 break;
2012             default:
2013                 error = cycle_color_attr(ch, fg, bg, tx);
2014                 break;
2015             }
2016         }
2017     } while (error);
2018     return result;
2019 }
2020
2021 static int
2022 x_attr_test(bool recur GCC_UNUSED)
2023 /* test text attributes using wide-character calls */
2024 {
2025     int n;
2026     int skip = get_xmc();
2027     NCURSES_COLOR_T fg = COLOR_BLACK;   /* color pair 0 is special */
2028     NCURSES_COLOR_T bg = COLOR_BLACK;
2029     NCURSES_COLOR_T tx = -1;
2030     int ac = 0;
2031     W_ATTR_TBL my_list[SIZEOF(w_attrs_to_test)];
2032     WINDOW *my_wins[SIZEOF(w_attrs_to_test)];
2033     unsigned my_size = init_w_attr_list(my_list, term_attrs());
2034
2035     if (my_size > 1) {
2036         unsigned j, k;
2037
2038         for (j = 0; j < my_size; ++j) {
2039             my_wins[j] = subwin(stdscr,
2040                                 1, LEN_ATTRSTRING,
2041                                 2 + (int) (2 * j), COL_ATTRSTRING);
2042             scrollok(my_wins[j], FALSE);
2043         }
2044
2045         if (skip < 0)
2046             skip = 0;
2047
2048         n = skip;               /* make it easy */
2049         k = my_size - 1;
2050         wide_init_attr_string();
2051
2052         do {
2053             int row = 2;
2054             NCURSES_PAIRS_T pair = 0;
2055             NCURSES_PAIRS_T extras = 0;
2056
2057             if (UseColors) {
2058                 pair = (NCURSES_PAIRS_T) (fg != COLOR_BLACK || bg != COLOR_BLACK);
2059                 if (pair != 0) {
2060                     pair = 1;
2061                     if (init_pair(pair, fg, bg) == ERR) {
2062                         beep();
2063                     }
2064                 }
2065                 extras = pair;
2066                 if (tx >= 0) {
2067                     extras = 2;
2068                     if (init_pair(extras, tx, bg) == ERR) {
2069                         beep();
2070                     }
2071                 }
2072             }
2073             set_wide_background(pair);
2074             erase();
2075
2076             box_set(stdscr, 0, 0);
2077             MvAddStr(0, 20, "Character attribute test display");
2078
2079             for (j = 0; j < my_size; ++j) {
2080                 row = wide_show_attr(my_wins[j], row, n, (j == k),
2081                                      ((attr_t) ac |
2082                                       my_list[j].attr |
2083                                       my_list[k].attr),
2084                                      extras,
2085                                      my_list[j].name);
2086             }
2087
2088             MvPrintw(row, COLS_PRE_ATTRS,
2089                      "This terminal does %shave the magic-cookie glitch",
2090                      get_xmc() > -1 ? "" : "not ");
2091             MvPrintw(row + 1, COLS_PRE_ATTRS, "Enter '?' for help.");
2092             show_color_attr(fg, bg, tx);
2093             printw("  ACS (%d)", ac != 0);
2094
2095             refresh();
2096         } while (wide_attr_getc(&n, &fg, &bg, &tx, &ac, &k, my_size));
2097
2098         set_wide_background(0);
2099         erase();
2100         endwin();
2101         return OK;
2102     } else {
2103         Cannot("does not support extended video attributes.");
2104         return ERR;
2105     }
2106 }
2107 #endif
2108
2109 /****************************************************************************
2110  *
2111  * Color support tests
2112  *
2113  ****************************************************************************/
2114
2115 static NCURSES_CONST char *the_color_names[] =
2116 {
2117     "black",
2118     "red",
2119     "green",
2120     "yellow",
2121     "blue",
2122     "magenta",
2123     "cyan",
2124     "white",
2125     "BLACK",
2126     "RED",
2127     "GREEN",
2128     "YELLOW",
2129     "BLUE",
2130     "MAGENTA",
2131     "CYAN",
2132     "WHITE"
2133 };
2134
2135 static void
2136 show_color_name(int y, int x, int color, bool wide, int zoom)
2137 {
2138     if (move(y, x) != ERR) {
2139         char temp[80];
2140         int width = 8;
2141
2142         if (wide || zoom) {
2143             _nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp))
2144                         "%02d", color);
2145             if (wide)
2146                 width = 4;
2147             if ((int) strlen(temp) >= width) {
2148                 int pwr2 = 0;
2149                 while ((1 << pwr2) < color)
2150                     ++pwr2;
2151                 _nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp))
2152                             width > 4 ? "2^%d" : "^%d", pwr2);
2153             }
2154         } else if (color >= 8) {
2155             _nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp))
2156                         "[%02d]", color);
2157         } else if (color < 0) {
2158             _nc_STRCPY(temp, "default", sizeof(temp));
2159         } else {
2160             _nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp))
2161                         "%.*s", 16, the_color_names[color]);
2162         }
2163         printw("%-*.*s", width, width, temp);
2164     }
2165 }
2166
2167 static void
2168 color_legend(WINDOW *helpwin, bool wide)
2169 {
2170     int row = 1;
2171     int col = 1;
2172
2173     MvWPrintw(helpwin, row++, col,
2174               "ESC to exit.");
2175     ++row;
2176     MvWPrintw(helpwin, row++, col,
2177               "Use up/down arrow to scroll through the display if it is");
2178     MvWPrintw(helpwin, row++, col,
2179               "longer than one screen. Control/N and Control/P can be used");
2180     MvWPrintw(helpwin, row++, col,
2181               "in place of up/down arrow.  Use pageup/pagedown to scroll a");
2182     MvWPrintw(helpwin, row++, col,
2183               "full screen; control/B and control/F can be used here.");
2184     ++row;
2185     MvWPrintw(helpwin, row++, col,
2186               "Toggles:");
2187     MvWPrintw(helpwin, row++, col,
2188               "  a/A     toggle altcharset off/on");
2189     MvWPrintw(helpwin, row++, col,
2190               "  b/B     toggle bold off/on");
2191     if (has_colors()) {
2192         MvWPrintw(helpwin, row++, col,
2193                   "  c/C     cycle used-colors through 8,16,...,COLORS");
2194     }
2195     MvWPrintw(helpwin, row++, col,
2196               "  n/N     toggle text/number on/off");
2197     MvWPrintw(helpwin, row++, col,
2198               "  r/R     toggle reverse on/off");
2199     MvWPrintw(helpwin, row++, col,
2200               "  w/W     switch width between 4/8 columns");
2201     MvWPrintw(helpwin, row++, col,
2202               "  z/Z     zoom out (or in)");
2203 #if USE_WIDEC_SUPPORT
2204     if (wide) {
2205         MvWPrintw(helpwin, row++, col,
2206                   "Wide characters:");
2207         MvWPrintw(helpwin, row, col,
2208                   "  x/X     toggle text between ASCII and wide-character");
2209     }
2210 #else
2211     (void) wide;
2212 #endif
2213 }
2214
2215 #define set_color_test(name, value) if (name != value) { name = value; base_row = 0; }
2216
2217 static int
2218 color_cycle(int current, int step)
2219 {
2220     int result = current;
2221     if (step < 0) {
2222         if (current <= 8) {
2223             result = COLORS;
2224         } else {
2225             result = 8;
2226             if ((result * 2) > COLORS) {
2227                 result = COLORS;
2228             } else {
2229                 while ((result * 2) < current) {
2230                     result *= 2;
2231                 }
2232             }
2233         }
2234     } else {
2235         if (current >= COLORS) {
2236             result = 8;
2237         } else {
2238             result *= 2;
2239         }
2240         if (result > COLORS)
2241             result = COLORS;
2242     }
2243     return result;
2244 }
2245
2246 /* generate a color test pattern */
2247 static int
2248 color_test(bool recur GCC_UNUSED)
2249 {
2250     NCURSES_PAIRS_T i;
2251     int top = 0, width;
2252     int base_row = 0;
2253     int grid_top = top + 3;
2254     int page_size = (LINES - grid_top);
2255     int pairs_max;
2256     int colors_max = COLORS;
2257     int col_limit;
2258     int row_limit;
2259     int per_row;
2260     char *numbered = 0;
2261     const char *hello;
2262     bool done = FALSE;
2263     bool opt_acsc = FALSE;
2264     bool opt_bold = FALSE;
2265     bool opt_revs = FALSE;
2266     bool opt_nums = FALSE;
2267     bool opt_wide = FALSE;
2268     int opt_zoom = 0;
2269     WINDOW *helpwin;
2270
2271     if (!UseColors) {
2272         Cannot("does not support color.");
2273         return ERR;
2274     }
2275
2276     numbered = typeCalloc(char, COLS + 1);
2277     done = ((COLS < 16) || (numbered == 0));
2278
2279     /*
2280      * Because the number of colors is usually a power of two, we also use
2281      * a power of two for the number of colors shown per line (to be tidy).
2282      */
2283     for (col_limit = 1; col_limit * 2 < COLS; col_limit *= 2) ;
2284
2285   reloop:
2286     while (!done) {
2287         int shown = 0;
2288         int zoom_size = (1 << opt_zoom);
2289         int colors_max1 = colors_max / zoom_size;
2290         double colors_max2 = (double) colors_max1 * (double) colors_max1;
2291
2292         pairs_max = PAIR_NUMBER(A_COLOR) + 1;
2293         if (colors_max2 <= COLOR_PAIRS) {
2294             int limit = (colors_max1 - MinColors) * (colors_max1 - MinColors);
2295             if (pairs_max > limit)
2296                 pairs_max = limit;
2297         }
2298         if (pairs_max > COLOR_PAIRS)
2299             pairs_max = COLOR_PAIRS;
2300         if (pairs_max < colors_max1)
2301             pairs_max = colors_max1;
2302
2303         /* this assumes an 80-column line */
2304         if (opt_wide) {
2305             width = 4;
2306             hello = "Test";
2307             per_row = (col_limit / ((colors_max1 > 8) ? width : 8));
2308         } else {
2309             width = 8;
2310             hello = "Hello";
2311             per_row = (col_limit / width);
2312         }
2313         per_row -= MinColors;
2314
2315         row_limit = (pairs_max + per_row - 1) / per_row;
2316
2317         move(0, 0);
2318         (void) printw("There are %d color pairs and %d colors",
2319                       pairs_max, COLORS);
2320         if (colors_max1 != COLORS)
2321             (void) printw(" (using %d colors)", colors_max1);
2322         if (MinColors)
2323             (void) addstr(" besides 'default'");
2324         if (opt_zoom)
2325             (void) printw(" zoom:%d", opt_zoom);
2326
2327         clrtobot();
2328         MvPrintw(top + 1, 0,
2329                  "%dx%d matrix of foreground/background colors, bold *%s*\n",
2330                  row_limit,
2331                  per_row,
2332                  opt_bold ? "on" : "off");
2333
2334         /* show color names/numbers across the top */
2335         for (i = 0; i < per_row; i++) {
2336             show_color_name(top + 2,
2337                             (i + 1) * width,
2338                             (int) i * zoom_size + MinColors,
2339                             opt_wide,
2340                             opt_zoom);
2341         }
2342
2343         /* show a grid of colors, with color names/ numbers on the left */
2344         for (i = (NCURSES_PAIRS_T) (base_row * per_row); i < pairs_max; i++) {
2345             int row = grid_top + (i / per_row) - base_row;
2346             int col = (i % per_row + 1) * width;
2347             NCURSES_PAIRS_T pair = i;
2348
2349             if ((i / per_row) > row_limit)
2350                 break;
2351
2352 #define InxToFG(i) (int)((((unsigned long)(i) * (unsigned long)zoom_size) % (unsigned long)(colors_max1 - MinColors)) + (unsigned long)MinColors)
2353 #define InxToBG(i) (int)((((unsigned long)(i) * (unsigned long)zoom_size) / (unsigned long)(colors_max1 - MinColors)) + (unsigned long)MinColors)
2354             if (row >= 0 && move(row, col) != ERR) {
2355                 NCURSES_COLOR_T fg = (NCURSES_COLOR_T) InxToFG(i);
2356                 NCURSES_COLOR_T bg = (NCURSES_COLOR_T) InxToBG(i);
2357
2358                 init_pair(pair, fg, bg);
2359                 attron(COLOR_PAIR(pair));
2360                 if (opt_acsc)
2361                     attron(A_ALTCHARSET);
2362                 if (opt_bold)
2363                     attron(A_BOLD);
2364                 if (opt_revs)
2365                     attron(A_REVERSE);
2366
2367                 if (opt_nums) {
2368                     _nc_SPRINTF(numbered, _nc_SLIMIT((size_t) (COLS + 1))
2369                                 "{%02X}", (int) i);
2370                     hello = numbered;
2371                 }
2372                 printw("%-*.*s", width, width, hello);
2373                 (void) attrset(A_NORMAL);
2374
2375                 if ((i % per_row) == 0 && InxToFG(i) == MinColors) {
2376                     show_color_name(row, 0,
2377                                     InxToBG(i),
2378                                     opt_wide,
2379                                     opt_zoom);
2380                 }
2381                 ++shown;
2382             } else if (shown) {
2383                 break;
2384             }
2385         }
2386
2387         switch (wGetchar(stdscr)) {
2388         case 'a':
2389             opt_acsc = FALSE;
2390             break;
2391         case 'A':
2392             opt_acsc = TRUE;
2393             break;
2394         case 'b':
2395             opt_bold = FALSE;
2396             break;
2397         case 'B':
2398             opt_bold = TRUE;
2399             break;
2400         case 'c':
2401             colors_max = color_cycle(colors_max, -1);
2402             break;
2403         case 'C':
2404             colors_max = color_cycle(colors_max, 1);
2405             break;
2406         case 'n':
2407             opt_nums = FALSE;
2408             break;
2409         case 'N':
2410             opt_nums = TRUE;
2411             break;
2412         case 'r':
2413             opt_revs = FALSE;
2414             break;
2415         case 'R':
2416             opt_revs = TRUE;
2417             break;
2418         case case_QUIT:
2419             done = TRUE;
2420             continue;
2421         case 'w':
2422             set_color_test(opt_wide, FALSE);
2423             break;
2424         case 'W':
2425             set_color_test(opt_wide, TRUE);
2426             break;
2427         case 'z':
2428             if (opt_zoom <= 0) {
2429                 beep();
2430             } else {
2431                 --opt_zoom;
2432                 goto reloop;
2433             }
2434             break;
2435         case 'Z':
2436             if ((1 << opt_zoom) >= colors_max) {
2437                 beep();
2438             } else {
2439                 ++opt_zoom;
2440                 goto reloop;
2441             }
2442             break;
2443         case CTRL('p'):
2444         case KEY_UP:
2445             if (base_row <= 0) {
2446                 beep();
2447             } else {
2448                 base_row -= 1;
2449             }
2450             break;
2451         case CTRL('n'):
2452         case KEY_DOWN:
2453             if (base_row + page_size >= row_limit) {
2454                 beep();
2455             } else {
2456                 base_row += 1;
2457             }
2458             break;
2459         case CTRL('b'):
2460         case KEY_PREVIOUS:
2461         case KEY_PPAGE:
2462             if (base_row <= 0) {
2463                 beep();
2464             } else {
2465                 base_row -= (page_size - 1);
2466                 if (base_row < 0)
2467                     base_row = 0;
2468             }
2469             break;
2470         case CTRL('f'):
2471         case KEY_NEXT:
2472         case KEY_NPAGE:
2473             if (base_row + page_size >= row_limit) {
2474                 beep();
2475             } else {
2476                 base_row += page_size - 1;
2477                 if (base_row + page_size >= row_limit) {
2478                     base_row = row_limit - page_size - 1;
2479                 }
2480             }
2481             break;
2482         case HELP_KEY_1:
2483             if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
2484                 box(helpwin, 0, 0);
2485                 color_legend(helpwin, FALSE);
2486                 wGetchar(helpwin);
2487                 delwin(helpwin);
2488             }
2489             break;
2490         default:
2491             beep();
2492             continue;
2493         }
2494     }
2495
2496     erase();
2497     endwin();
2498
2499     free(numbered);
2500     return OK;
2501 }
2502
2503 #if USE_WIDEC_SUPPORT
2504
2505 #if USE_EXTENDED_COLOR
2506 #define InitExtendedPair(p,f,g) init_extended_pair((p),(f),(g))
2507 #define ExtendedColorSet(p)     color_set((NCURSES_PAIRS_T) (p), &(p))
2508 #define EXTENDED_PAIRS_T int
2509 #else
2510 #define InitExtendedPair(p,f,g) init_pair((NCURSES_PAIRS_T) (p),(NCURSES_COLOR_T)(f),(NCURSES_COLOR_T)(g))
2511 #define ExtendedColorSet(p)     color_set((NCURSES_PAIRS_T) (p), NULL)
2512 #define EXTENDED_PAIRS_T NCURSES_PAIRS_T
2513 #endif
2514
2515 /* generate a color test pattern */
2516 static int
2517 x_color_test(bool recur GCC_UNUSED)
2518 {
2519     long i;
2520     int top = 0, width;
2521     int base_row = 0;
2522     int grid_top = top + 3;
2523     int page_size = (LINES - grid_top);
2524     int pairs_max;
2525     int colors_max = COLORS;
2526     int col_limit;
2527     int row_limit;
2528     int per_row;
2529     char *numbered = 0;
2530     const char *hello;
2531     bool done = FALSE;
2532     bool opt_acsc = FALSE;
2533     bool opt_bold = FALSE;
2534     bool opt_revs = FALSE;
2535     bool opt_wide = FALSE;
2536     bool opt_nums = FALSE;
2537     bool opt_xchr = FALSE;
2538     int opt_zoom = 0;
2539     wchar_t *buffer = 0;
2540     WINDOW *helpwin;
2541
2542     if (!UseColors) {
2543         Cannot("does not support color.");
2544         return ERR;
2545     }
2546     numbered = typeCalloc(char, COLS + 1);
2547     buffer = typeCalloc(wchar_t, COLS + 1);
2548     done = ((COLS < 16) || (numbered == 0) || (buffer == 0));
2549
2550     /*
2551      * Because the number of colors is usually a power of two, we also use
2552      * a power of two for the number of colors shown per line (to be tidy).
2553      */
2554     for (col_limit = 1; col_limit * 2 < COLS; col_limit *= 2) ;
2555
2556   reloop:
2557     while (!done) {
2558         int shown = 0;
2559         int zoom_size = (1 << opt_zoom);
2560         int colors_max1 = colors_max / zoom_size;
2561         double colors_max2 = (double) colors_max1 * (double) colors_max1;
2562
2563         pairs_max = ((unsigned) (-1)) / 2;
2564         if (colors_max2 <= COLOR_PAIRS) {
2565             int limit = (colors_max1 - MinColors) * (colors_max1 - MinColors);
2566             if (pairs_max > limit)
2567                 pairs_max = limit;
2568         }
2569         if (pairs_max > COLOR_PAIRS)
2570             pairs_max = COLOR_PAIRS;
2571         if (pairs_max < colors_max1)
2572             pairs_max = colors_max1;
2573
2574         if (opt_wide) {
2575             width = 4;
2576             hello = "Test";
2577             per_row = (col_limit / ((colors_max1 > 8) ? width : 8));
2578         } else {
2579             width = 8;
2580             hello = "Hello";
2581             per_row = (col_limit / width);
2582         }
2583         per_row -= MinColors;
2584
2585         if (opt_xchr) {
2586             make_fullwidth_text(buffer, hello);
2587             width *= 2;
2588             per_row /= 2;
2589         } else {
2590             make_narrow_text(buffer, hello);
2591         }
2592
2593         row_limit = (pairs_max + per_row - 1) / per_row;
2594
2595         move(0, 0);
2596         (void) printw("There are %d color pairs and %d colors",
2597                       pairs_max, COLORS);
2598         if (colors_max1 != COLORS)
2599             (void) printw(" (using %d colors)", colors_max1);
2600         if (MinColors)
2601             (void) addstr(" besides 'default'");
2602         if (opt_zoom)
2603             (void) printw(" zoom:%d", opt_zoom);
2604
2605         clrtobot();
2606         MvPrintw(top + 1, 0,
2607                  "%dx%d matrix of foreground/background colors, bold *%s*\n",
2608                  row_limit,
2609                  per_row,
2610                  opt_bold ? "on" : "off");
2611
2612         /* show color names/numbers across the top */
2613         for (i = 0; i < per_row; i++) {
2614             show_color_name(top + 2,
2615                             ((int) i + 1) * width,
2616                             (int) i * zoom_size + MinColors,
2617                             opt_wide,
2618                             opt_zoom);
2619         }
2620
2621         /* show a grid of colors, with color names/ numbers on the left */
2622         for (i = (base_row * per_row); i < pairs_max; i++) {
2623             int row = grid_top + ((int) i / per_row) - base_row;
2624             int col = ((int) i % per_row + 1) * width;
2625             int pair = (int) i;
2626
2627             if ((i / per_row) > row_limit)
2628                 break;
2629
2630             if (row >= 0 && move(row, col) != ERR) {
2631                 InitExtendedPair(pair, InxToFG(i), InxToBG(i));
2632                 (void) ExtendedColorSet(pair);
2633                 if (opt_acsc)
2634                     attr_on(WA_ALTCHARSET, NULL);
2635                 if (opt_bold)
2636                     attr_on(WA_BOLD, NULL);
2637                 if (opt_revs)
2638                     attr_on(WA_REVERSE, NULL);
2639
2640                 if (opt_nums) {
2641                     _nc_SPRINTF(numbered,
2642                                 _nc_SLIMIT((size_t) (COLS + 1) * sizeof(wchar_t))
2643                                 "{%02X}", (unsigned) i);
2644                     if (opt_xchr) {
2645                         make_fullwidth_text(buffer, numbered);
2646                     } else {
2647                         make_narrow_text(buffer, numbered);
2648                     }
2649                 }
2650                 addnwstr(buffer, width);
2651                 (void) attr_set(A_NORMAL, 0, NULL);
2652
2653                 if ((i % per_row) == 0 && InxToFG(i) == MinColors) {
2654                     show_color_name(row, 0,
2655                                     InxToBG(i),
2656                                     opt_wide,
2657                                     opt_zoom);
2658                 }
2659                 ++shown;
2660             } else if (shown) {
2661                 break;
2662             }
2663         }
2664
2665         switch (wGetchar(stdscr)) {
2666         case 'a':
2667             opt_acsc = FALSE;
2668             break;
2669         case 'A':
2670             opt_acsc = TRUE;
2671             break;
2672         case 'b':
2673             opt_bold = FALSE;
2674             break;
2675         case 'B':
2676             opt_bold = TRUE;
2677             break;
2678         case 'c':
2679             colors_max = color_cycle(colors_max, -1);
2680             break;
2681         case 'C':
2682             colors_max = color_cycle(colors_max, 1);
2683             break;
2684         case 'n':
2685             opt_nums = FALSE;
2686             break;
2687         case 'N':
2688             opt_nums = TRUE;
2689             break;
2690         case 'r':
2691             opt_revs = FALSE;
2692             break;
2693         case 'R':
2694             opt_revs = TRUE;
2695             break;
2696         case case_QUIT:
2697             done = TRUE;
2698             continue;
2699         case 'w':
2700             set_color_test(opt_wide, FALSE);
2701             break;
2702         case 'W':
2703             set_color_test(opt_wide, TRUE);
2704             break;
2705         case 'x':
2706             opt_xchr = FALSE;
2707             break;
2708         case 'X':
2709             opt_xchr = TRUE;
2710             break;
2711         case 'z':
2712             if (opt_zoom <= 0) {
2713                 beep();
2714             } else {
2715                 --opt_zoom;
2716                 goto reloop;
2717             }
2718             break;
2719         case 'Z':
2720             if ((1 << opt_zoom) >= colors_max) {
2721                 beep();
2722             } else {
2723                 ++opt_zoom;
2724                 goto reloop;
2725             }
2726             break;
2727         case CTRL('p'):
2728         case KEY_UP:
2729             if (base_row <= 0) {
2730                 beep();
2731             } else {
2732                 base_row -= 1;
2733             }
2734             break;
2735         case CTRL('n'):
2736         case KEY_DOWN:
2737             if (base_row + page_size >= row_limit) {
2738                 beep();
2739             } else {
2740                 base_row += 1;
2741             }
2742             break;
2743         case CTRL('b'):
2744         case KEY_PREVIOUS:
2745         case KEY_PPAGE:
2746             if (base_row <= 0) {
2747                 beep();
2748             } else {
2749                 base_row -= (page_size - 1);
2750                 if (base_row < 0)
2751                     base_row = 0;
2752             }
2753             break;
2754         case CTRL('f'):
2755         case KEY_NEXT:
2756         case KEY_NPAGE:
2757             if (base_row + page_size >= row_limit) {
2758                 beep();
2759             } else {
2760                 base_row += page_size - 1;
2761                 if (base_row + page_size >= row_limit) {
2762                     base_row = row_limit - page_size - 1;
2763                 }
2764             }
2765             break;
2766         case HELP_KEY_1:
2767             if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
2768                 box(helpwin, 0, 0);
2769                 color_legend(helpwin, TRUE);
2770                 wGetchar(helpwin);
2771                 delwin(helpwin);
2772             }
2773             break;
2774         default:
2775             beep();
2776             continue;
2777         }
2778     }
2779
2780     erase();
2781     endwin();
2782
2783     free(numbered);
2784     free(buffer);
2785     return OK;
2786 }
2787 #endif /* USE_WIDEC_SUPPORT */
2788
2789 #if HAVE_COLOR_CONTENT
2790 static void
2791 change_color(NCURSES_PAIRS_T current, int field, int value, int usebase)
2792 {
2793     NCURSES_COLOR_T red, green, blue;
2794
2795     color_content(current, &red, &green, &blue);
2796
2797     switch (field) {
2798     case 0:
2799         red = (NCURSES_COLOR_T) (usebase ? (red + value) : value);
2800         break;
2801     case 1:
2802         green = (NCURSES_COLOR_T) (usebase ? (green + value) : value);
2803         break;
2804     case 2:
2805         blue = (NCURSES_COLOR_T) (usebase ? (blue + value) : value);
2806         break;
2807     }
2808
2809     if (init_color(current, red, green, blue) == ERR)
2810         beep();
2811 }
2812
2813 static void
2814 reset_all_colors(void)
2815 {
2816     NCURSES_PAIRS_T c;
2817
2818     for (c = 0; c < COLORS; ++c)
2819         init_color(c,
2820                    all_colors[c].red,
2821                    all_colors[c].green,
2822                    all_colors[c].blue);
2823 }
2824
2825 #define okCOLOR(n) ((n) >= 0 && (n) < MaxColors)
2826 #define okRGB(n)   ((n) >= 0 && (n) <= 1000)
2827 #define DecodeRGB(n) (NCURSES_COLOR_T) ((n * 1000) / 0xffff)
2828
2829 static void
2830 init_all_colors(bool xterm_colors, char *palette_file)
2831 {
2832     NCURSES_PAIRS_T cp;
2833     all_colors = typeMalloc(RGB_DATA, (unsigned) MaxColors);
2834     if (!all_colors)
2835         failed("all_colors");
2836     for (cp = 0; cp < MaxColors; ++cp) {
2837         color_content(cp,
2838                       &all_colors[cp].red,
2839                       &all_colors[cp].green,
2840                       &all_colors[cp].blue);
2841     }
2842     /* xterm and compatible terminals can read results of an OSC string
2843      * asking for the current color palette.
2844      */
2845     if (xterm_colors) {
2846         int n;
2847         char result[BUFSIZ];
2848         int check_n;
2849         unsigned check_r, check_g, check_b;
2850
2851         raw();
2852         noecho();
2853
2854         for (n = 0; n < MaxColors; ++n) {
2855             int got;
2856
2857             fprintf(stderr, "\033]4;%d;?\007", n);
2858             got = (int) read(0, result, sizeof(result) - 1);
2859             if (got < 0)
2860                 break;
2861             result[got] = '\0';
2862             if (sscanf(result, "\033]4;%d;rgb:%x/%x/%x\007",
2863                        &check_n,
2864                        &check_r,
2865                        &check_g,
2866                        &check_b) == 4 &&
2867                 check_n == n) {
2868                 all_colors[n].red = DecodeRGB(check_r);
2869                 all_colors[n].green = DecodeRGB(check_g);
2870                 all_colors[n].blue = DecodeRGB(check_b);
2871             } else {
2872                 break;
2873             }
2874         }
2875         reset_prog_mode();
2876     }
2877     if (palette_file != 0) {
2878         FILE *fp = fopen(palette_file, "r");
2879         if (fp != 0) {
2880             char buffer[BUFSIZ];
2881             int red, green, blue;
2882             int scale = 1000;
2883             int c;
2884             while (fgets(buffer, sizeof(buffer), fp) != 0) {
2885                 if (sscanf(buffer, "scale:%d", &c) == 1) {
2886                     scale = c;
2887                 } else if (sscanf(buffer, "%d:%d %d %d",
2888                                   &c,
2889                                   &red,
2890                                   &green,
2891                                   &blue) == 4
2892                            && okCOLOR(c)
2893                            && okRGB(red)
2894                            && okRGB(green)
2895                            && okRGB(blue)) {
2896 #define Scaled(n) (NCURSES_COLOR_T) (((n) * 1000) / scale)
2897                     all_colors[c].red = Scaled(red);
2898                     all_colors[c].green = Scaled(green);
2899                     all_colors[c].blue = Scaled(blue);
2900                 }
2901             }
2902             fclose(fp);
2903         }
2904     }
2905 }
2906
2907 #define scaled_rgb(n) ((255 * (n)) / 1000)
2908
2909 static int
2910 color_edit(bool recur GCC_UNUSED)
2911 /* display the color test pattern, without trying to edit colors */
2912 {
2913     int i;
2914     int current;
2915     int this_c, value, field;
2916     int last_c;
2917     int top_color;
2918     int page_size;
2919
2920     if (!UseColors) {
2921         Cannot("does not support color.");
2922         return ERR;
2923     } else if (!can_change_color()) {
2924         Cannot("has hardwired color values.");
2925         return ERR;
2926     }
2927
2928     reset_all_colors();
2929 #ifdef KEY_RESIZE
2930   retry:
2931 #endif
2932     current = 0;
2933     this_c = 0;
2934     value = 0;
2935     field = 0;
2936     top_color = 0;
2937     page_size = (LINES - 6);
2938     erase();
2939
2940     for (i = 0; i < MaxColors; i++)
2941         init_pair((NCURSES_PAIRS_T) i,
2942                   (NCURSES_COLOR_T) COLOR_WHITE,
2943                   (NCURSES_COLOR_T) i);
2944
2945     MvPrintw(LINES - 2, 0, "Number: %d", value);
2946
2947     do {
2948         NCURSES_COLOR_T red, green, blue;
2949
2950         attron(A_BOLD);
2951         MvAddStr(0, 20, "Color RGB Value Editing");
2952         attroff(A_BOLD);
2953
2954         for (i = (NCURSES_COLOR_T) top_color;
2955              (i - top_color < page_size)
2956              && (i < MaxColors); i++) {
2957             char numeric[80];
2958
2959             _nc_SPRINTF(numeric, _nc_SLIMIT(sizeof(numeric)) "[%d]", i);
2960             MvPrintw(2 + i - top_color, 0, "%c %-8s:",
2961                      (i == current ? '>' : ' '),
2962                      (i < (int) SIZEOF(the_color_names)
2963                       ? the_color_names[i] : numeric));
2964             (void) attrset(AttrArg(COLOR_PAIR(i), 0));
2965             addstr("        ");
2966             (void) attrset(A_NORMAL);
2967
2968             color_content((NCURSES_PAIRS_T) i, &red, &green, &blue);
2969             addstr("   R = ");
2970             if (current == i && field == 0)
2971                 attron(A_STANDOUT);
2972             printw("%04d", (int) red);
2973             if (current == i && field == 0)
2974                 (void) attrset(A_NORMAL);
2975             addstr(", G = ");
2976             if (current == i && field == 1)
2977                 attron(A_STANDOUT);
2978             printw("%04d", (int) green);
2979             if (current == i && field == 1)
2980                 (void) attrset(A_NORMAL);
2981             addstr(", B = ");
2982             if (current == i && field == 2)
2983                 attron(A_STANDOUT);
2984             printw("%04d", (int) blue);
2985             if (current == i && field == 2)
2986                 (void) attrset(A_NORMAL);
2987             (void) attrset(A_NORMAL);
2988             printw(" ( %3d %3d %3d )",
2989                    (int) scaled_rgb(red),
2990                    (int) scaled_rgb(green),
2991                    (int) scaled_rgb(blue));
2992         }
2993
2994         MvAddStr(LINES - 3, 0,
2995                  "Use up/down to select a color, left/right to change fields.");
2996         MvAddStr(LINES - 2, 0,
2997                  "Modify field by typing nnn=, nnn-, or nnn+.  ? for help.");
2998
2999         move(2 + current - top_color, 0);
3000
3001         last_c = this_c;
3002         this_c = Getchar();
3003         if (this_c < 256 && isdigit(this_c) && !isdigit(last_c))
3004             value = 0;
3005
3006         switch (this_c) {
3007 #ifdef KEY_RESIZE
3008         case KEY_RESIZE:
3009             move(0, 0);
3010             goto retry;
3011 #endif
3012         case '!':
3013             ShellOut(FALSE);
3014             /* FALLTHRU */
3015         case CTRL('r'):
3016             endwin();
3017             refresh();
3018             break;
3019         case CTRL('l'):
3020             refresh();
3021             break;
3022         case CTRL('b'):
3023         case KEY_PPAGE:
3024             if (current > 0)
3025                 current -= (page_size - 1);
3026             else
3027                 beep();
3028             break;
3029
3030         case CTRL('f'):
3031         case KEY_NPAGE:
3032             if (current < (MaxColors - 1))
3033                 current += (page_size - 1);
3034             else
3035                 beep();
3036             break;
3037
3038         case CTRL('p'):
3039         case KEY_UP:
3040             current = (current == 0 ? (MaxColors - 1) : current - 1);
3041             break;
3042
3043         case CTRL('n'):
3044         case KEY_DOWN:
3045             current = (current == (MaxColors - 1) ? 0 : current + 1);
3046             break;
3047
3048         case '\t':
3049         case KEY_RIGHT:
3050             field = (field == 2 ? 0 : field + 1);
3051             break;
3052
3053         case KEY_BTAB:
3054         case KEY_LEFT:
3055             field = (field == 0 ? 2 : field - 1);
3056             break;
3057
3058         case '0':
3059         case '1':
3060         case '2':
3061         case '3':
3062         case '4':
3063         case '5':
3064         case '6':
3065         case '7':
3066         case '8':
3067         case '9':
3068             value = value * 10 + (this_c - '0');
3069             break;
3070
3071         case '+':
3072             change_color((NCURSES_PAIRS_T) current, field, value, 1);
3073             break;
3074
3075         case '-':
3076             change_color((NCURSES_PAIRS_T) current, field, -value, 1);
3077             break;
3078
3079         case '=':
3080             change_color((NCURSES_PAIRS_T) current, field, value, 0);
3081             break;
3082
3083         case HELP_KEY_1:
3084             erase();
3085             P("                      RGB Value Editing Help");
3086             P("");
3087             P("You are in the RGB value editor.  Use the arrow keys to select one of");
3088             P("the fields in one of the RGB triples of the current colors; the one");
3089             P("currently selected will be reverse-video highlighted.");
3090             P("");
3091             P("To change a field, enter the digits of the new value; they are echoed");
3092             P("as entered.  Finish by typing `='.  The change will take effect instantly.");
3093             P("To increment or decrement a value, use the same procedure, but finish");
3094             P("with a `+' or `-'.");
3095             P("");
3096             P("Use `!' to shell-out, ^R or ^L to repaint the screen.");
3097             P("");
3098             P("Press 'm' to invoke the top-level menu with the current color settings.");
3099             P("To quit, do ESC");
3100
3101             Pause();
3102             erase();
3103             break;
3104
3105         case 'm':
3106             endwin();
3107             main_menu(FALSE);
3108             for (i = 0; i < MaxColors; i++)
3109                 init_pair((NCURSES_PAIRS_T) i,
3110                           (NCURSES_COLOR_T) COLOR_WHITE,
3111                           (NCURSES_COLOR_T) i);
3112             refresh();
3113             break;
3114
3115         case case_QUIT:
3116             break;
3117
3118         default:
3119             beep();
3120             break;
3121         }
3122
3123         if (current < 0)
3124             current = 0;
3125         if (current >= MaxColors)
3126             current = MaxColors - 1;
3127         if (current < top_color)
3128             top_color = current;
3129         if (current - top_color >= page_size)
3130             top_color = current - (page_size - 1);
3131
3132         MvPrintw(LINES - 1, 0, "Number: %d", value);
3133         clrtoeol();
3134     } while
3135         (!isQuit(this_c, TRUE));
3136
3137     erase();
3138
3139     /*
3140      * ncurses does not reset each color individually when calling endwin().
3141      */
3142     reset_all_colors();
3143
3144     endwin();
3145     return OK;
3146 }
3147 #endif /* HAVE_COLOR_CONTENT */
3148
3149 /****************************************************************************
3150  *
3151  * Alternate character-set stuff
3152  *
3153  ****************************************************************************/
3154 static bool
3155 cycle_attr(int ch, unsigned *at_code, chtype *attr, ATTR_TBL * list, unsigned limit)
3156 {
3157     bool result = TRUE;
3158
3159     switch (ch) {
3160     case 'v':
3161         if ((*at_code += 1) >= limit)
3162             *at_code = 0;
3163         break;
3164     case 'V':
3165         if (*at_code == 0)
3166             *at_code = limit - 1;
3167         else
3168             *at_code -= 1;
3169         break;
3170     default:
3171         result = FALSE;
3172         break;
3173     }
3174     if (result)
3175         *attr = list[*at_code].attr;
3176     return result;
3177 }
3178
3179 #if USE_WIDEC_SUPPORT
3180 static bool
3181 cycle_w_attr(int ch, unsigned *at_code, attr_t *attr, W_ATTR_TBL * list, unsigned limit)
3182 {
3183     bool result = TRUE;
3184
3185     switch (ch) {
3186     case 'v':
3187         if ((*at_code += 1) >= limit)
3188             *at_code = 0;
3189         break;
3190     case 'V':
3191         if (*at_code == 0)
3192             *at_code = limit - 1;
3193         else
3194             *at_code -= 1;
3195         break;
3196     default:
3197         result = FALSE;
3198         break;
3199     }
3200     if (result)
3201         *attr = list[*at_code].attr;
3202     return result;
3203 }
3204 #endif
3205
3206 static bool
3207 cycle_colors(int ch, int *fg, int *bg, NCURSES_PAIRS_T *pair)
3208 {
3209     bool result = FALSE;
3210
3211     if (UseColors) {
3212         result = TRUE;
3213         switch (ch) {
3214         case 'F':
3215             if ((*fg -= 1) < 0)
3216                 *fg = COLORS - 1;
3217             break;
3218         case 'f':
3219             if ((*fg += 1) >= COLORS)
3220                 *fg = 0;
3221             break;
3222         case 'B':
3223             if ((*bg -= 1) < 0)
3224                 *bg = COLORS - 1;
3225             break;
3226         case 'b':
3227             if ((*bg += 1) >= COLORS)
3228                 *bg = 0;
3229             break;
3230         default:
3231             result = FALSE;
3232             break;
3233         }
3234         if (result) {
3235             *pair = (NCURSES_PAIRS_T) (*fg != COLOR_BLACK || *bg != COLOR_BLACK);
3236             if (*pair != 0) {
3237                 *pair = 1;
3238                 if (init_pair(*pair,
3239                               (NCURSES_COLOR_T) *fg,
3240                               (NCURSES_COLOR_T) *bg) == ERR) {
3241                     result = FALSE;
3242                 }
3243             }
3244         }
3245     }
3246     return result;
3247 }
3248
3249 /****************************************************************************
3250  *
3251  * Soft-key label test
3252  *
3253  ****************************************************************************/
3254
3255 #if USE_SOFTKEYS
3256
3257 #define SLK_HELP 17
3258 #define SLK_WORK (SLK_HELP + 3)
3259
3260 static void
3261 slk_help(void)
3262 {
3263     static const char *table[] =
3264     {
3265         "Available commands are:"
3266         ,""
3267         ,"^L         -- repaint this message and activate soft keys"
3268         ,"a/d        -- activate/disable soft keys"
3269         ,"c          -- set centered format for labels"
3270         ,"l          -- set left-justified format for labels"
3271         ,"r          -- set right-justified format for labels"
3272         ,"[12345678] -- set label; labels are numbered 1 through 8"
3273         ,"e          -- erase stdscr (should not erase labels)"
3274         ,"s          -- test scrolling of shortened screen"
3275         ,"v/V        -- cycle through video attributes"
3276 #if HAVE_SLK_COLOR
3277         ,"F/f/B/b    -- cycle through foreground/background colors"
3278 #endif
3279         ,"ESC        -- return to main menu"
3280         ,""
3281         ,"Note: if activating the soft keys causes your terminal to scroll up"
3282         ,"one line, your terminal auto-scrolls when anything is written to the"
3283         ,"last screen position.  The ncurses code does not yet handle this"
3284         ,"gracefully."
3285     };
3286     unsigned j;
3287
3288     move(2, 0);
3289     for (j = 0; j < SIZEOF(table); ++j) {
3290         P(table[j]);
3291     }
3292     refresh();
3293 }
3294
3295 #if HAVE_SLK_COLOR
3296 static void
3297 call_slk_color(int fg, int bg)
3298 {
3299     init_pair(1, (NCURSES_COLOR_T) bg, (NCURSES_COLOR_T) fg);
3300     slk_color(1);
3301     MvPrintw(SLK_WORK, 0, "Colors %d/%d\n", fg, bg);
3302     clrtoeol();
3303     slk_touch();
3304     slk_noutrefresh();
3305     refresh();
3306 }
3307 #endif
3308
3309 static int
3310 slk_test(bool recur GCC_UNUSED)
3311 /* exercise the soft keys */
3312 {
3313     int c, fmt = 1;
3314     char buf[9];
3315     char *s;
3316     chtype attr = A_NORMAL;
3317     unsigned at_code = 0;
3318 #if HAVE_SLK_COLOR
3319     int fg = COLOR_BLACK;
3320     int bg = COLOR_WHITE;
3321     NCURSES_PAIRS_T pair = 0;
3322 #endif
3323     ATTR_TBL my_list[SIZEOF(attrs_to_test)];
3324     unsigned my_size = init_attr_list(my_list, termattrs());
3325
3326     c = CTRL('l');
3327 #if HAVE_SLK_COLOR
3328     if (UseColors) {
3329         call_slk_color(fg, bg);
3330     }
3331 #endif
3332
3333     do {
3334         move(0, 0);
3335         switch (c) {
3336         case CTRL('l'):
3337             erase();
3338             attron(A_BOLD);
3339             MvAddStr(0, 20, "Soft Key Exerciser");
3340             attroff(A_BOLD);
3341
3342             slk_help();
3343             /* fall through */
3344
3345         case 'a':
3346             slk_restore();
3347             break;
3348
3349         case 'e':
3350             wclear(stdscr);
3351             break;
3352
3353         case 's':
3354             MvPrintw(SLK_WORK, 0, "Press Q to stop the scrolling-test: ");
3355             while ((c = Getchar()) != 'Q' && (c != ERR))
3356                 AddCh(c);
3357             break;
3358
3359         case 'd':
3360             slk_clear();
3361             break;
3362
3363         case 'l':
3364             fmt = 0;
3365             break;
3366
3367         case 'c':
3368             fmt = 1;
3369             break;
3370
3371         case 'r':
3372             fmt = 2;
3373             break;
3374
3375         case '1':
3376         case '2':
3377         case '3':
3378         case '4':
3379         case '5':
3380         case '6':
3381         case '7':
3382         case '8':
3383             MvAddStr(SLK_WORK, 0, "Please enter the label value: ");
3384             _nc_STRCPY(buf, "", sizeof(buf));
3385             if ((s = slk_label(c - '0')) != 0) {
3386                 _nc_STRNCPY(buf, s, (size_t) 8);
3387             }
3388             wGetstring(stdscr, buf, 8);
3389             slk_set((c - '0'), buf, fmt);
3390             slk_refresh();
3391             move(SLK_WORK, 0);
3392             clrtobot();
3393             break;
3394
3395         case case_QUIT:
3396             goto done;
3397
3398 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
3399         case KEY_RESIZE:
3400             wnoutrefresh(stdscr);
3401             break;
3402 #endif
3403
3404         default:
3405             if (cycle_attr(c, &at_code, &attr, my_list, my_size)) {
3406                 slk_attrset(attr);
3407                 slk_touch();
3408                 slk_noutrefresh();
3409                 break;
3410             }
3411 #if HAVE_SLK_COLOR
3412             if (cycle_colors(c, &fg, &bg, &pair)) {
3413                 if (UseColors) {
3414                     call_slk_color(fg, bg);
3415                 } else {
3416                     beep();
3417                 }
3418                 break;
3419             }
3420 #endif
3421             beep();
3422             break;
3423         }
3424     } while (!isQuit(c = Getchar(), TRUE));
3425
3426   done:
3427     slk_clear();
3428     erase();
3429     endwin();
3430     return OK;
3431 }
3432
3433 #if USE_WIDEC_SUPPORT
3434 #define SLKLEN 8
3435 static int
3436 x_slk_test(bool recur GCC_UNUSED)
3437 /* exercise the soft keys */
3438 {
3439     int c, fmt = 1;
3440     wchar_t buf[SLKLEN + 1];
3441     char *s;
3442     attr_t attr = WA_NORMAL;
3443     unsigned at_code = 0;
3444     int fg = COLOR_BLACK;
3445     int bg = COLOR_WHITE;
3446     NCURSES_PAIRS_T pair = 0;
3447     W_ATTR_TBL my_list[SIZEOF(w_attrs_to_test)];
3448     unsigned my_size = init_w_attr_list(my_list, term_attrs());
3449
3450     c = CTRL('l');
3451     if (UseColors) {
3452         call_slk_color(fg, bg);
3453     }
3454     do {
3455         move(0, 0);
3456         switch (c) {
3457         case CTRL('l'):
3458             erase();
3459             attr_on(WA_BOLD, NULL);
3460             MvAddStr(0, 20, "Soft Key Exerciser");
3461             attr_off(WA_BOLD, NULL);
3462
3463             slk_help();
3464             /* fall through */
3465
3466         case 'a':
3467             slk_restore();
3468             break;
3469
3470         case 'e':
3471             wclear(stdscr);
3472             break;
3473
3474         case 's':
3475             MvPrintw(SLK_WORK, 0, "Press Q to stop the scrolling-test: ");
3476             while ((c = Getchar()) != 'Q' && (c != ERR))
3477                 AddCh(c);
3478             break;
3479
3480         case 'd':
3481             slk_clear();
3482             break;
3483
3484         case 'l':
3485             fmt = 0;
3486             break;
3487
3488         case 'c':
3489             fmt = 1;
3490             break;
3491
3492         case 'r':
3493             fmt = 2;
3494             break;
3495
3496         case '1':
3497         case '2':
3498         case '3':
3499         case '4':
3500         case '5':
3501         case '6':
3502         case '7':
3503         case '8':
3504             MvAddStr(SLK_WORK, 0, "Please enter the label value: ");
3505             *buf = 0;
3506             if ((s = slk_label(c - '0')) != 0) {
3507                 char *temp = strdup(s);
3508                 size_t used = strlen(temp);
3509                 size_t want = SLKLEN;
3510 #ifndef state_unused
3511                 mbstate_t state;
3512 #endif
3513
3514                 buf[0] = L'\0';
3515                 while (want > 0 && used != 0) {
3516                     size_t test;
3517                     const char *base = s;
3518
3519                     reset_mbytes(state);
3520                     test = count_mbytes(base, 0, &state);
3521                     if (test == (size_t) -1) {
3522                         temp[--used] = 0;
3523                     } else if (test > want) {
3524                         temp[--used] = 0;
3525                     } else {
3526                         reset_mbytes(state);
3527                         trans_mbytes(buf, base, want, &state);
3528                         break;
3529                     }
3530                 }
3531                 free(temp);
3532             }
3533             wGet_wstring(stdscr, buf, SLKLEN);
3534             slk_wset((c - '0'), buf, fmt);
3535             slk_refresh();
3536             move(SLK_WORK, 0);
3537             clrtobot();
3538             break;
3539
3540         case case_QUIT:
3541             goto done;
3542
3543         case 'F':
3544             if (UseColors) {
3545                 fg = (NCURSES_COLOR_T) ((fg + 1) % COLORS);
3546                 call_slk_color(fg, bg);
3547             }
3548             break;
3549         case 'B':
3550             if (UseColors) {
3551                 bg = (NCURSES_COLOR_T) ((bg + 1) % COLORS);
3552                 call_slk_color(fg, bg);
3553             }
3554             break;
3555 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
3556         case KEY_RESIZE:
3557             wnoutrefresh(stdscr);
3558             break;
3559 #endif
3560         default:
3561             if (cycle_w_attr(c, &at_code, &attr, my_list, my_size)) {
3562                 slk_attr_set(attr, (NCURSES_COLOR_T) (fg || bg), NULL);
3563                 slk_touch();
3564                 slk_noutrefresh();
3565                 break;
3566             }
3567 #if HAVE_SLK_COLOR
3568             if (cycle_colors(c, &fg, &bg, &pair)) {
3569                 if (UseColors) {
3570                     call_slk_color(fg, bg);
3571                 } else {
3572                     beep();
3573                 }
3574                 break;
3575             }
3576 #endif
3577             beep();
3578             break;
3579         }
3580     } while (!isQuit(c = Getchar(), TRUE));
3581
3582   done:
3583     slk_clear();
3584     erase();
3585     endwin();
3586     return OK;
3587 }
3588 #endif
3589 #endif /* SLK_INIT */
3590
3591 static void
3592 show_256_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3593 {
3594     unsigned first = 0;
3595     unsigned last = 255;
3596     unsigned code;
3597     int count;
3598
3599     erase();
3600     attron(A_BOLD);
3601     MvPrintw(0, 20, "Display of Character Codes %#0x to %#0x",
3602              first, last);
3603     attroff(A_BOLD);
3604     refresh();
3605
3606     for (code = first; code <= last; ++code) {
3607         int row = (int) (2 + (code / 16));
3608         int col = (int) (5 * (code % 16));
3609         IGNORE_RC(mvaddch(row, col, colored_chtype(code, attr, pair)));
3610         for (count = 1; count < repeat; ++count) {
3611             AddCh(colored_chtype(code, attr, pair));
3612         }
3613     }
3614
3615 }
3616
3617 /*
3618  * Show a slice of 32 characters, allowing those to be repeated up to the
3619  * screen's width.
3620  *
3621  * ISO 6429:  codes 0x80 to 0x9f may be control characters that cause the
3622  * terminal to perform functions.  The remaining codes can be graphic.
3623  */
3624 static void
3625 show_upper_chars(int base, int pagesize, int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3626 {
3627     unsigned code;
3628     unsigned first = (unsigned) base;
3629     unsigned last = first + (unsigned) pagesize - 2;
3630     bool C1 = (first == 128);
3631     int reply;
3632
3633     erase();
3634     attron(A_BOLD);
3635     MvPrintw(0, 20, "Display of %s Character Codes %d to %d",
3636              C1 ? "C1" : "GR", first, last);
3637     attroff(A_BOLD);
3638     refresh();
3639
3640     for (code = first; code <= last; code++) {
3641         int count = repeat;
3642         int row = 2 + ((int) (code - first) % (pagesize / 2));
3643         int col = ((int) (code - first) / (pagesize / 2)) * COLS / 2;
3644         char tmp[80];
3645         _nc_SPRINTF(tmp, _nc_SLIMIT(sizeof(tmp)) "%3u (0x%x)", code, code);
3646         MvPrintw(row, col, "%*s: ", COLS / 4, tmp);
3647
3648         do {
3649             if (C1)
3650                 nodelay(stdscr, TRUE);
3651             echochar(colored_chtype(code, attr, pair));
3652             if (C1) {
3653                 /* (yes, this _is_ crude) */
3654                 while ((reply = Getchar()) != ERR) {
3655                     AddCh(UChar(reply));
3656                     napms(10);
3657                 }
3658                 nodelay(stdscr, FALSE);
3659             }
3660         } while (--count > 0);
3661     }
3662 }
3663
3664 #define PC_COLS 4
3665
3666 static void
3667 show_pc_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3668 {
3669     unsigned code;
3670
3671     erase();
3672     attron(A_BOLD);
3673     MvPrintw(0, 20, "Display of PC Character Codes");
3674     attroff(A_BOLD);
3675     refresh();
3676
3677     for (code = 0; code < 16; ++code) {
3678         MvPrintw(2, (int) code * PC_COLS + 8, "%X", code);
3679     }
3680     for (code = 0; code < 256; code++) {
3681         int count = repeat;
3682         int row = 3 + (int) (code / 16) + (code >= 128);
3683         int col = 8 + (int) (code % 16) * PC_COLS;
3684         if ((code % 16) == 0)
3685             MvPrintw(row, 0, "0x%02x:", code);
3686         move(row, col);
3687         do {
3688             switch (code) {
3689             case '\n':
3690             case '\r':
3691             case '\b':
3692             case '\f':
3693             case '\033':
3694             case 0x9b:
3695                 /*
3696                  * Skip the ones that do not work.
3697                  */
3698                 break;
3699             default:
3700                 AddCh(colored_chtype(code, A_ALTCHARSET | attr, pair));
3701                 break;
3702             }
3703         } while (--count > 0);
3704     }
3705 }
3706
3707 static void
3708 show_box_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3709 {
3710     (void) repeat;
3711
3712     attr |= (attr_t) COLOR_PAIR(pair);
3713
3714     erase();
3715     attron(A_BOLD);
3716     MvAddStr(0, 20, "Display of the ACS Line-Drawing Set");
3717     attroff(A_BOLD);
3718     refresh();
3719     /* *INDENT-OFF* */
3720     wborder(stdscr,
3721             colored_chtype(ACS_VLINE,    attr, pair),
3722             colored_chtype(ACS_VLINE,    attr, pair),
3723             colored_chtype(ACS_HLINE,    attr, pair),
3724             colored_chtype(ACS_HLINE,    attr, pair),
3725             colored_chtype(ACS_ULCORNER, attr, pair),
3726             colored_chtype(ACS_URCORNER, attr, pair),
3727             colored_chtype(ACS_LLCORNER, attr, pair),
3728             colored_chtype(ACS_LRCORNER, attr, pair));
3729     MvHLine(LINES / 2, 0,        colored_chtype(ACS_HLINE, attr, pair), COLS);
3730     MvVLine(0,         COLS / 2, colored_chtype(ACS_VLINE, attr, pair), LINES);
3731     MvAddCh(0,         COLS / 2, colored_chtype(ACS_TTEE,  attr, pair));
3732     MvAddCh(LINES / 2, COLS / 2, colored_chtype(ACS_PLUS,  attr, pair));
3733     MvAddCh(LINES - 1, COLS / 2, colored_chtype(ACS_BTEE,  attr, pair));
3734     MvAddCh(LINES / 2, 0,        colored_chtype(ACS_LTEE,  attr, pair));
3735     MvAddCh(LINES / 2, COLS - 1, colored_chtype(ACS_RTEE,  attr, pair));
3736     /* *INDENT-ON* */
3737
3738 }
3739
3740 static int
3741 show_1_acs(int n, int repeat, const char *name, chtype code)
3742 {
3743     const int height = 16;
3744     int row = 2 + (n % height);
3745     int col = (n / height) * COLS / 2;
3746
3747     MvPrintw(row, col, "%*s : ", COLS / 4, name);
3748     do {
3749         AddCh(code);
3750     } while (--repeat > 0);
3751     return n + 1;
3752 }
3753
3754 static void
3755 show_acs_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3756 /* display the ACS character set */
3757 {
3758     int n;
3759
3760 #define BOTH(name) #name, colored_chtype(name, attr, (chtype) pair)
3761
3762     erase();
3763     attron(A_BOLD);
3764     MvAddStr(0, 20, "Display of the ACS Character Set");
3765     attroff(A_BOLD);
3766     refresh();
3767
3768     n = show_1_acs(0, repeat, BOTH(ACS_ULCORNER));
3769     n = show_1_acs(n, repeat, BOTH(ACS_URCORNER));
3770     n = show_1_acs(n, repeat, BOTH(ACS_LLCORNER));
3771     n = show_1_acs(n, repeat, BOTH(ACS_LRCORNER));
3772
3773     n = show_1_acs(n, repeat, BOTH(ACS_LTEE));
3774     n = show_1_acs(n, repeat, BOTH(ACS_RTEE));
3775     n = show_1_acs(n, repeat, BOTH(ACS_TTEE));
3776     n = show_1_acs(n, repeat, BOTH(ACS_BTEE));
3777
3778     n = show_1_acs(n, repeat, BOTH(ACS_HLINE));
3779     n = show_1_acs(n, repeat, BOTH(ACS_VLINE));
3780
3781     /*
3782      * HPUX's ACS definitions are broken here.  Just give up.
3783      */
3784 #if !(defined(__hpux) && !defined(NCURSES_VERSION))
3785     n = show_1_acs(n, repeat, BOTH(ACS_LARROW));
3786     n = show_1_acs(n, repeat, BOTH(ACS_RARROW));
3787     n = show_1_acs(n, repeat, BOTH(ACS_UARROW));
3788     n = show_1_acs(n, repeat, BOTH(ACS_DARROW));
3789
3790     n = show_1_acs(n, repeat, BOTH(ACS_BLOCK));
3791     n = show_1_acs(n, repeat, BOTH(ACS_BOARD));
3792     n = show_1_acs(n, repeat, BOTH(ACS_LANTERN));
3793     n = show_1_acs(n, repeat, BOTH(ACS_BULLET));
3794     n = show_1_acs(n, repeat, BOTH(ACS_CKBOARD));
3795     n = show_1_acs(n, repeat, BOTH(ACS_DEGREE));
3796     n = show_1_acs(n, repeat, BOTH(ACS_DIAMOND));
3797     n = show_1_acs(n, repeat, BOTH(ACS_PLMINUS));
3798     n = show_1_acs(n, repeat, BOTH(ACS_PLUS));
3799
3800     n = show_1_acs(n, repeat, BOTH(ACS_GEQUAL));
3801     n = show_1_acs(n, repeat, BOTH(ACS_NEQUAL));
3802     n = show_1_acs(n, repeat, BOTH(ACS_LEQUAL));
3803
3804     n = show_1_acs(n, repeat, BOTH(ACS_STERLING));
3805     n = show_1_acs(n, repeat, BOTH(ACS_PI));
3806     n = show_1_acs(n, repeat, BOTH(ACS_S1));
3807     n = show_1_acs(n, repeat, BOTH(ACS_S3));
3808     n = show_1_acs(n, repeat, BOTH(ACS_S7));
3809     (void) show_1_acs(n, repeat, BOTH(ACS_S9));
3810 #endif
3811 #undef BOTH
3812 }
3813
3814 static int
3815 acs_test(bool recur GCC_UNUSED)
3816 {
3817     int c = 'a';
3818     int pagesize = 32;
3819     char *term = getenv("TERM");
3820     const char *pch_kludge = ((term != 0 && strstr(term, "linux"))
3821                               ? "p=PC, "
3822                               : "");
3823     chtype attr = A_NORMAL;
3824     int digit = 0;
3825     int repeat = 1;
3826     int fg = COLOR_BLACK;
3827     int bg = COLOR_BLACK;
3828     unsigned at_code = 0;
3829     NCURSES_PAIRS_T pair = 0;
3830     void (*last_show_acs) (int, attr_t, NCURSES_PAIRS_T) = 0;
3831     ATTR_TBL my_list[SIZEOF(attrs_to_test)];
3832     unsigned my_size = init_attr_list(my_list, termattrs());
3833
3834     do {
3835         switch (c) {
3836         case CTRL('L'):
3837             Repaint();
3838             break;
3839         case 'a':
3840             ToggleAcs(last_show_acs, show_acs_chars);
3841             break;
3842         case 'p':
3843             if (*pch_kludge)
3844                 ToggleAcs(last_show_acs, show_pc_chars);
3845             else
3846                 beep();
3847             break;
3848         case 'w':
3849             if (pagesize == 32) {
3850                 pagesize = 256;
3851             } else {
3852                 pagesize = 32;
3853             }
3854             break;
3855         case 'x':
3856             ToggleAcs(last_show_acs, show_box_chars);
3857             break;
3858         case '0':
3859         case '1':
3860         case '2':
3861         case '3':
3862             digit = (c - '0');
3863             last_show_acs = 0;
3864             break;
3865         case '-':
3866             if (digit > 0) {
3867                 --digit;
3868                 last_show_acs = 0;
3869             } else {
3870                 beep();
3871             }
3872             break;
3873         case '+':
3874             if (digit < 3) {
3875                 ++digit;
3876                 last_show_acs = 0;
3877             } else {
3878                 beep();
3879             }
3880             break;
3881         case '>':
3882             if (repeat < (COLS / 4))
3883                 ++repeat;
3884             break;
3885         case '<':
3886             if (repeat > 1)
3887                 --repeat;
3888             break;
3889         default:
3890             if (cycle_attr(c, &at_code, &attr, my_list, my_size)
3891                 || cycle_colors(c, &fg, &bg, &pair)) {
3892                 break;
3893             } else {
3894                 beep();
3895             }
3896             break;
3897         }
3898         if (pagesize != 32) {
3899             show_256_chars(repeat, attr, pair);
3900         } else if (last_show_acs != 0) {
3901             last_show_acs(repeat, attr, pair);
3902         } else {
3903             show_upper_chars(digit * pagesize + 128, pagesize, repeat, attr, pair);
3904         }
3905
3906         MvPrintw(LINES - 3, 0,
3907                  "Note: ANSI terminals may not display C1 characters.");
3908         MvPrintw(LINES - 2, 0,
3909                  "Select: a=ACS, w=all x=box, %s0=C1, 1-3,+/- non-ASCII, </> repeat, ESC=quit",
3910                  pch_kludge);
3911         if (UseColors) {
3912             MvPrintw(LINES - 1, 0,
3913                      "v/V, f/F, b/B cycle through video attributes (%s) and color %d/%d.",
3914                      my_list[at_code].name,
3915                      fg, bg);
3916         } else {
3917             MvPrintw(LINES - 1, 0,
3918                      "v/V cycles through video attributes (%s).",
3919                      my_list[at_code].name);
3920         }
3921         refresh();
3922     } while (!isQuit(c = Getchar(), TRUE));
3923
3924     Pause();
3925     erase();
3926     endwin();
3927     return OK;
3928 }
3929
3930 #if USE_WIDEC_SUPPORT
3931 static cchar_t *
3932 merge_wide_attr(cchar_t *dst, const cchar_t *src, attr_t attr, NCURSES_PAIRS_T pair)
3933 {
3934
3935     *dst = *src;
3936     do {
3937         int count;
3938         TEST_CCHAR(src, count, {
3939             attr |= (test_attrs & A_ALTCHARSET);
3940             setcchar(dst, test_wch, attr, pair, NULL);
3941         }, {
3942             ;
3943         });
3944     } while (0);
3945     return dst;
3946 }
3947
3948 /*
3949  * Header/legend take up no more than 8 lines, leaving 16 lines on a 24-line
3950  * display.  If there are no repeats, we could normally display 16 lines of 64
3951  * characters (1024 total).  However, taking repeats and double-width cells
3952  * into account, use 256 characters for the page.
3953  */
3954 static void
3955 show_paged_widechars(unsigned base,
3956                      unsigned pagesize,
3957                      int repeat,
3958                      int space,
3959                      attr_t attr,
3960                      NCURSES_PAIRS_T pair)
3961 {
3962     unsigned first = base * pagesize;
3963     unsigned last = first + pagesize - 1;
3964     int per_line = 16;
3965     cchar_t temp;
3966     wchar_t code;
3967     wchar_t codes[10];
3968
3969     erase();
3970     attron(A_BOLD);
3971     MvPrintw(0, 20, "Display of Character Codes %#x to %#x", first, last);
3972     attroff(A_BOLD);
3973
3974     for (code = (wchar_t) first; code <= (wchar_t) last; code++) {
3975         int row = (2 + (int) (code - (wchar_t) first) / per_line);
3976         int col = 5 * ((int) code % per_line);
3977         int count;
3978
3979         memset(&codes, 0, sizeof(codes));
3980         codes[0] = code;
3981         setcchar(&temp, codes, attr, pair, 0);
3982         move(row, col);
3983         if (wcwidth(code) == 0 && code != 0) {
3984             AddCh((chtype) space |
3985                   (A_REVERSE ^ attr) |
3986                   (attr_t) COLOR_PAIR(pair));
3987         }
3988         add_wch(&temp);
3989         for (count = 1; count < repeat; ++count) {
3990             add_wch(&temp);
3991         }
3992     }
3993 }
3994
3995 static void
3996 show_upper_widechars(unsigned first, int repeat, int space, attr_t attr, NCURSES_PAIRS_T pair)
3997 {
3998     cchar_t temp;
3999     wchar_t code;
4000     unsigned last = first + 31;
4001
4002     erase();
4003     attron(A_BOLD);
4004     MvPrintw(0, 20, "Display of Character Codes %d to %d", first, last);
4005     attroff(A_BOLD);
4006
4007     for (code = (wchar_t) first; code <= (wchar_t) last; code++) {
4008         int row = 2 + ((int) (code - (wchar_t) first) % 16);
4009         int col = ((int) (code - (wchar_t) first) / 16) * COLS / 2;
4010         wchar_t codes[10];
4011         char tmp[80];
4012         int count = repeat;
4013
4014         _nc_SPRINTF(tmp, _nc_SLIMIT(sizeof(tmp))
4015                     "%3ld (0x%lx)", (long) code, (long) code);
4016         MvPrintw(row, col, "%*s: ", COLS / 4, tmp);
4017
4018         memset(&codes, 0, sizeof(codes));
4019         codes[0] = code;
4020         setcchar(&temp, codes, attr, pair, 0);
4021
4022         do {
4023             int y, x;
4024
4025             /*
4026              * Give non-spacing characters something to combine with.  If we
4027              * don't, they'll bunch up in a heap on the space after the ":".
4028              * Mark them with reverse-video to make them simpler to find on
4029              * the display.
4030              */
4031             if (wcwidth(code) == 0) {
4032                 AddCh((chtype) space |
4033                       (A_REVERSE ^ attr) |
4034                       (attr_t) COLOR_PAIR(pair));
4035             }
4036             /*
4037              * This uses echo_wchar(), for comparison with the normal 'f'
4038              * test (and to make a test-case for echo_wchar()).  The screen
4039              * may flicker because the erase() at the top of the function
4040              * is met by the builtin refresh() in echo_wchar().
4041              */
4042             echo_wchar(&temp);
4043             /*
4044              * The repeat-count may make text wrap - avoid that.
4045              */
4046             getyx(stdscr, y, x);
4047             (void) y;
4048             if (x >= col + (COLS / 2) - 2)
4049                 break;
4050         } while (--count > 0);
4051     }
4052 }
4053
4054 static int
4055 show_1_wacs(int n, int repeat, const char *name, const cchar_t *code)
4056 {
4057     const int height = 16;
4058     int row = 2 + (n % height);
4059     int col = (n / height) * COLS / 2;
4060
4061     MvPrintw(row, col, "%*s : ", COLS / 4, name);
4062     while (--repeat >= 0) {
4063         add_wch(code);
4064     }
4065     return n + 1;
4066 }
4067
4068 #define MERGE_ATTR(wch) merge_wide_attr(&temp, wch, attr, pair)
4069
4070 static void
4071 show_wacs_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
4072 /* display the wide-ACS character set */
4073 {
4074     cchar_t temp;
4075
4076     int n;
4077
4078 /*#define BOTH2(name) #name, &(name) */
4079 #define BOTH2(name) #name, MERGE_ATTR(name)
4080
4081     erase();
4082     attron(A_BOLD);
4083     MvAddStr(0, 20, "Display of the Wide-ACS Character Set");
4084     attroff(A_BOLD);
4085     refresh();
4086
4087     n = show_1_wacs(0, repeat, BOTH2(WACS_ULCORNER));
4088     n = show_1_wacs(n, repeat, BOTH2(WACS_URCORNER));
4089     n = show_1_wacs(n, repeat, BOTH2(WACS_LLCORNER));
4090     n = show_1_wacs(n, repeat, BOTH2(WACS_LRCORNER));
4091
4092     n = show_1_wacs(n, repeat, BOTH2(WACS_LTEE));
4093     n = show_1_wacs(n, repeat, BOTH2(WACS_RTEE));
4094     n = show_1_wacs(n, repeat, BOTH2(WACS_TTEE));
4095     n = show_1_wacs(n, repeat, BOTH2(WACS_BTEE));
4096
4097     n = show_1_wacs(n, repeat, BOTH2(WACS_HLINE));
4098     n = show_1_wacs(n, repeat, BOTH2(WACS_VLINE));
4099
4100     n = show_1_wacs(n, repeat, BOTH2(WACS_LARROW));
4101     n = show_1_wacs(n, repeat, BOTH2(WACS_RARROW));
4102     n = show_1_wacs(n, repeat, BOTH2(WACS_UARROW));
4103     n = show_1_wacs(n, repeat, BOTH2(WACS_DARROW));
4104
4105     n = show_1_wacs(n, repeat, BOTH2(WACS_BLOCK));
4106     n = show_1_wacs(n, repeat, BOTH2(WACS_BOARD));
4107     n = show_1_wacs(n, repeat, BOTH2(WACS_LANTERN));
4108     n = show_1_wacs(n, repeat, BOTH2(WACS_BULLET));
4109     n = show_1_wacs(n, repeat, BOTH2(WACS_CKBOARD));
4110     n = show_1_wacs(n, repeat, BOTH2(WACS_DEGREE));
4111     n = show_1_wacs(n, repeat, BOTH2(WACS_DIAMOND));
4112     n = show_1_wacs(n, repeat, BOTH2(WACS_PLMINUS));
4113     n = show_1_wacs(n, repeat, BOTH2(WACS_PLUS));
4114
4115 #ifdef CURSES_WACS_ARRAY
4116     n = show_1_wacs(n, repeat, BOTH2(WACS_GEQUAL));
4117     n = show_1_wacs(n, repeat, BOTH2(WACS_NEQUAL));
4118     n = show_1_wacs(n, repeat, BOTH2(WACS_LEQUAL));
4119
4120     n = show_1_wacs(n, repeat, BOTH2(WACS_STERLING));
4121     n = show_1_wacs(n, repeat, BOTH2(WACS_PI));
4122     n = show_1_wacs(n, repeat, BOTH2(WACS_S1));
4123     n = show_1_wacs(n, repeat, BOTH2(WACS_S3));
4124     n = show_1_wacs(n, repeat, BOTH2(WACS_S7));
4125     (void) show_1_wacs(n, repeat, BOTH2(WACS_S9));
4126 #endif
4127 }
4128
4129 #ifdef WACS_D_PLUS
4130 static void
4131 show_wacs_chars_double(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
4132 /* display the wide-ACS character set */
4133 {
4134     cchar_t temp;
4135
4136     int n;
4137
4138 /*#define BOTH2(name) #name, &(name) */
4139 #define BOTH2(name) #name, MERGE_ATTR(name)
4140
4141     erase();
4142     attron(A_BOLD);
4143     MvAddStr(0, 20, "Display of the Wide-ACS Character Set");
4144     attroff(A_BOLD);
4145     refresh();
4146
4147     n = show_1_wacs(0, repeat, BOTH2(WACS_D_ULCORNER));
4148     n = show_1_wacs(n, repeat, BOTH2(WACS_D_URCORNER));
4149     n = show_1_wacs(n, repeat, BOTH2(WACS_D_LLCORNER));
4150     n = show_1_wacs(n, repeat, BOTH2(WACS_D_LRCORNER));
4151
4152     n = show_1_wacs(n, repeat, BOTH2(WACS_D_LTEE));
4153     n = show_1_wacs(n, repeat, BOTH2(WACS_D_RTEE));
4154     n = show_1_wacs(n, repeat, BOTH2(WACS_D_TTEE));
4155     n = show_1_wacs(n, repeat, BOTH2(WACS_D_BTEE));
4156
4157     n = show_1_wacs(n, repeat, BOTH2(WACS_D_HLINE));
4158     n = show_1_wacs(n, repeat, BOTH2(WACS_D_VLINE));
4159
4160     n = show_1_wacs(n, repeat, BOTH2(WACS_LARROW));
4161     n = show_1_wacs(n, repeat, BOTH2(WACS_RARROW));
4162     n = show_1_wacs(n, repeat, BOTH2(WACS_UARROW));
4163     n = show_1_wacs(n, repeat, BOTH2(WACS_DARROW));
4164
4165     n = show_1_wacs(n, repeat, BOTH2(WACS_BLOCK));
4166     n = show_1_wacs(n, repeat, BOTH2(WACS_BOARD));
4167     n = show_1_wacs(n, repeat, BOTH2(WACS_LANTERN));
4168     n = show_1_wacs(n, repeat, BOTH2(WACS_BULLET));
4169     n = show_1_wacs(n, repeat, BOTH2(WACS_CKBOARD));
4170     n = show_1_wacs(n, repeat, BOTH2(WACS_DEGREE));
4171     n = show_1_wacs(n, repeat, BOTH2(WACS_DIAMOND));
4172     n = show_1_wacs(n, repeat, BOTH2(WACS_PLMINUS));
4173     n = show_1_wacs(n, repeat, BOTH2(WACS_D_PLUS));
4174
4175 #ifdef CURSES_WACS_ARRAY
4176     n = show_1_wacs(n, repeat, BOTH2(WACS_GEQUAL));
4177     n = show_1_wacs(n, repeat, BOTH2(WACS_NEQUAL));
4178     n = show_1_wacs(n, repeat, BOTH2(WACS_LEQUAL));
4179
4180     n = show_1_wacs(n, repeat, BOTH2(WACS_STERLING));
4181     n = show_1_wacs(n, repeat, BOTH2(WACS_PI));
4182     n = show_1_wacs(n, repeat, BOTH2(WACS_S1));
4183     n = show_1_wacs(n, repeat, BOTH2(WACS_S3));
4184     n = show_1_wacs(n, repeat, BOTH2(WACS_S7));
4185     (void) show_1_wacs(n, repeat, BOTH2(WACS_S9));
4186 #endif
4187 }
4188 #endif
4189
4190 #ifdef WACS_T_PLUS
4191 static void
4192 show_wacs_chars_thick(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
4193 /* display the wide-ACS character set */
4194 {
4195     cchar_t temp;
4196
4197     int n;
4198
4199 /*#define BOTH2(name) #name, &(name) */
4200 #define BOTH2(name) #name, MERGE_ATTR(name)
4201
4202     erase();
4203     attron(A_BOLD);
4204     MvAddStr(0, 20, "Display of the Wide-ACS Character Set");
4205     attroff(A_BOLD);
4206     refresh();
4207
4208     n = show_1_wacs(0, repeat, BOTH2(WACS_T_ULCORNER));
4209     n = show_1_wacs(n, repeat, BOTH2(WACS_T_URCORNER));
4210     n = show_1_wacs(n, repeat, BOTH2(WACS_T_LLCORNER));
4211     n = show_1_wacs(n, repeat, BOTH2(WACS_T_LRCORNER));
4212
4213     n = show_1_wacs(n, repeat, BOTH2(WACS_T_LTEE));
4214     n = show_1_wacs(n, repeat, BOTH2(WACS_T_RTEE));
4215     n = show_1_wacs(n, repeat, BOTH2(WACS_T_TTEE));
4216     n = show_1_wacs(n, repeat, BOTH2(WACS_T_BTEE));
4217
4218     n = show_1_wacs(n, repeat, BOTH2(WACS_T_HLINE));
4219     n = show_1_wacs(n, repeat, BOTH2(WACS_T_VLINE));
4220
4221     n = show_1_wacs(n, repeat, BOTH2(WACS_LARROW));
4222     n = show_1_wacs(n, repeat, BOTH2(WACS_RARROW));
4223     n = show_1_wacs(n, repeat, BOTH2(WACS_UARROW));
4224     n = show_1_wacs(n, repeat, BOTH2(WACS_DARROW));
4225
4226     n = show_1_wacs(n, repeat, BOTH2(WACS_BLOCK));
4227     n = show_1_wacs(n, repeat, BOTH2(WACS_BOARD));
4228     n = show_1_wacs(n, repeat, BOTH2(WACS_LANTERN));
4229     n = show_1_wacs(n, repeat, BOTH2(WACS_BULLET));
4230     n = show_1_wacs(n, repeat, BOTH2(WACS_CKBOARD));
4231     n = show_1_wacs(n, repeat, BOTH2(WACS_DEGREE));
4232     n = show_1_wacs(n, repeat, BOTH2(WACS_DIAMOND));
4233     n = show_1_wacs(n, repeat, BOTH2(WACS_PLMINUS));
4234     n = show_1_wacs(n, repeat, BOTH2(WACS_T_PLUS));
4235
4236 #ifdef CURSES_WACS_ARRAY
4237     n = show_1_wacs(n, repeat, BOTH2(WACS_GEQUAL));
4238     n = show_1_wacs(n, repeat, BOTH2(WACS_NEQUAL));
4239     n = show_1_wacs(n, repeat, BOTH2(WACS_LEQUAL));
4240
4241     n = show_1_wacs(n, repeat, BOTH2(WACS_STERLING));
4242     n = show_1_wacs(n, repeat, BOTH2(WACS_PI));
4243     n = show_1_wacs(n, repeat, BOTH2(WACS_S1));
4244     n = show_1_wacs(n, repeat, BOTH2(WACS_S3));
4245     n = show_1_wacs(n, repeat, BOTH2(WACS_S7));
4246     (void) show_1_wacs(n, repeat, BOTH2(WACS_S9));
4247 #endif
4248 }
4249 #endif
4250
4251 #undef MERGE_ATTR
4252
4253 #define MERGE_ATTR(n,wch) merge_wide_attr(&temp[n], wch, attr, pair)
4254
4255 static void
4256 show_wbox_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
4257 {
4258     cchar_t temp[8];
4259
4260     (void) repeat;
4261     erase();
4262     attron(A_BOLD);
4263     MvAddStr(0, 20, "Display of the Wide-ACS Line-Drawing Set");
4264     attroff(A_BOLD);
4265     refresh();
4266
4267     wborder_set(stdscr,
4268                 MERGE_ATTR(0, WACS_VLINE),
4269                 MERGE_ATTR(1, WACS_VLINE),
4270                 MERGE_ATTR(2, WACS_HLINE),
4271                 MERGE_ATTR(3, WACS_HLINE),
4272                 MERGE_ATTR(4, WACS_ULCORNER),
4273                 MERGE_ATTR(5, WACS_URCORNER),
4274                 MERGE_ATTR(6, WACS_LLCORNER),
4275                 MERGE_ATTR(7, WACS_LRCORNER));
4276     /* *INDENT-OFF* */
4277     (void) mvhline_set(LINES / 2, 0,        MERGE_ATTR(0, WACS_HLINE), COLS);
4278     (void) mvvline_set(0,         COLS / 2, MERGE_ATTR(0, WACS_VLINE), LINES);
4279     (void) mvadd_wch(0,           COLS / 2, MERGE_ATTR(0, WACS_TTEE));
4280     (void) mvadd_wch(LINES / 2,   COLS / 2, MERGE_ATTR(0, WACS_PLUS));
4281     (void) mvadd_wch(LINES - 1,   COLS / 2, MERGE_ATTR(0, WACS_BTEE));
4282     (void) mvadd_wch(LINES / 2,   0,        MERGE_ATTR(0, WACS_LTEE));
4283     (void) mvadd_wch(LINES / 2,   COLS - 1, MERGE_ATTR(0, WACS_RTEE));
4284     /* *INDENT-ON* */
4285
4286 }
4287
4288 #undef MERGE_ATTR
4289
4290 static int
4291 show_2_wacs(int n, const char *name, const char *code, attr_t attr, NCURSES_PAIRS_T pair)
4292 {
4293     const int height = 16;
4294     int row = 2 + (n % height);
4295     int col = (n / height) * COLS / 2;
4296     char temp[80];
4297
4298     MvPrintw(row, col, "%*s : ", COLS / 4, name);
4299     (void) attr_set(attr, pair, 0);
4300     _nc_STRNCPY(temp, code, 20);
4301     addstr(temp);
4302     (void) attr_set(A_NORMAL, 0, 0);
4303     return n + 1;
4304 }
4305
4306 #define SHOW_UTF8(n, name, code) show_2_wacs(n, name, code, attr, pair)
4307
4308 static void
4309 show_utf8_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
4310 {
4311     int n;
4312
4313     (void) repeat;
4314     erase();
4315     attron(A_BOLD);
4316     MvAddStr(0, 20, "Display of the Wide-ACS Character Set");
4317     attroff(A_BOLD);
4318     refresh();
4319     /* *INDENT-OFF* */
4320     n = SHOW_UTF8(0, "WACS_ULCORNER",   "\342\224\214");
4321     n = SHOW_UTF8(n, "WACS_URCORNER",   "\342\224\220");
4322     n = SHOW_UTF8(n, "WACS_LLCORNER",   "\342\224\224");
4323     n = SHOW_UTF8(n, "WACS_LRCORNER",   "\342\224\230");
4324
4325     n = SHOW_UTF8(n, "WACS_LTEE",       "\342\224\234");
4326     n = SHOW_UTF8(n, "WACS_RTEE",       "\342\224\244");
4327     n = SHOW_UTF8(n, "WACS_TTEE",       "\342\224\254");
4328     n = SHOW_UTF8(n, "WACS_BTEE",       "\342\224\264");
4329
4330     n = SHOW_UTF8(n, "WACS_HLINE",      "\342\224\200");
4331     n = SHOW_UTF8(n, "WACS_VLINE",      "\342\224\202");
4332
4333     n = SHOW_UTF8(n, "WACS_LARROW",     "\342\206\220");
4334     n = SHOW_UTF8(n, "WACS_RARROW",     "\342\206\222");
4335     n = SHOW_UTF8(n, "WACS_UARROW",     "\342\206\221");
4336     n = SHOW_UTF8(n, "WACS_DARROW",     "\342\206\223");
4337
4338     n = SHOW_UTF8(n, "WACS_BLOCK",      "\342\226\256");
4339     n = SHOW_UTF8(n, "WACS_BOARD",      "\342\226\222");
4340     n = SHOW_UTF8(n, "WACS_LANTERN",    "\342\230\203");
4341     n = SHOW_UTF8(n, "WACS_BULLET",     "\302\267");
4342     n = SHOW_UTF8(n, "WACS_CKBOARD",    "\342\226\222");
4343     n = SHOW_UTF8(n, "WACS_DEGREE",     "\302\260");
4344     n = SHOW_UTF8(n, "WACS_DIAMOND",    "\342\227\206");
4345     n = SHOW_UTF8(n, "WACS_PLMINUS",    "\302\261");
4346     n = SHOW_UTF8(n, "WACS_PLUS",       "\342\224\274");
4347     n = SHOW_UTF8(n, "WACS_GEQUAL",     "\342\211\245");
4348     n = SHOW_UTF8(n, "WACS_NEQUAL",     "\342\211\240");
4349     n = SHOW_UTF8(n, "WACS_LEQUAL",     "\342\211\244");
4350
4351     n = SHOW_UTF8(n, "WACS_STERLING",   "\302\243");
4352     n = SHOW_UTF8(n, "WACS_PI",         "\317\200");
4353     n = SHOW_UTF8(n, "WACS_S1",         "\342\216\272");
4354     n = SHOW_UTF8(n, "WACS_S3",         "\342\216\273");
4355     n = SHOW_UTF8(n, "WACS_S7",         "\342\216\274");
4356     (void) SHOW_UTF8(n, "WACS_S9",      "\342\216\275");
4357     /* *INDENT-ON* */
4358
4359 }
4360
4361 /* display the wide-ACS character set */
4362 static int
4363 x_acs_test(bool recur GCC_UNUSED)
4364 {
4365     int c = 'a';
4366     unsigned digit = 0;
4367     int repeat = 1;
4368     int space = ' ';
4369     unsigned pagesize = 32;
4370     attr_t attr = WA_NORMAL;
4371     int fg = COLOR_BLACK;
4372     int bg = COLOR_BLACK;
4373     unsigned at_code = 0;
4374     NCURSES_PAIRS_T pair = 0;
4375     void (*last_show_wacs) (int, attr_t, NCURSES_PAIRS_T) = 0;
4376     W_ATTR_TBL my_list[SIZEOF(w_attrs_to_test)];
4377     unsigned my_size = init_w_attr_list(my_list, term_attrs());
4378     char at_page[20];
4379     bool pending_code = FALSE;
4380
4381     at_page[0] = '\0';
4382     do {
4383         switch (c) {
4384         case CTRL('L'):
4385             Repaint();
4386             break;
4387         case 'a':
4388             ToggleAcs(last_show_wacs, show_wacs_chars);
4389             break;
4390 #ifdef WACS_D_PLUS
4391         case 'd':
4392             ToggleAcs(last_show_wacs, show_wacs_chars_double);
4393             break;
4394 #endif
4395 #ifdef WACS_T_PLUS
4396         case 't':
4397             ToggleAcs(last_show_wacs, show_wacs_chars_thick);
4398             break;
4399 #endif
4400         case 'w':
4401             if (pagesize == 32) {
4402                 pagesize = 256;
4403             } else {
4404                 pagesize = 32;
4405             }
4406             break;
4407         case 'x':
4408             ToggleAcs(last_show_wacs, show_wbox_chars);
4409             break;
4410         case 'u':
4411             ToggleAcs(last_show_wacs, show_utf8_chars);
4412             break;
4413         case '@':
4414             pending_code = !pending_code;
4415             if (pending_code) {
4416                 _nc_SPRINTF(at_page, _nc_SLIMIT(sizeof(at_page)) "%02x", digit);
4417             } else if (at_page[0] != '\0') {
4418                 _nc_SPRINTF(at_page, _nc_SLIMIT(sizeof(at_page)) "%x", digit);
4419             }
4420             break;
4421         default:
4422             if (pending_code && isxdigit(c)) {
4423                 size_t len = strlen(at_page);
4424                 if (len && at_page[0] == '0') {
4425                     memmove(at_page, at_page + 1, len--);
4426                 }
4427                 if (len < sizeof(at_page) - 1) {
4428                     at_page[len++] = (char) c;
4429                     at_page[len] = '\0';
4430                 }
4431             } else if (pending_code
4432                        && (c == '\b' || c == KEY_BACKSPACE || c == KEY_DC)) {
4433                 size_t len = strlen(at_page);
4434                 if (len)
4435                     at_page[--len] = '\0';
4436             } else if (c < 256 && isdigit(c)) {
4437                 digit = (unsigned) (c - '0');
4438                 last_show_wacs = 0;
4439             } else if (c == '+') {
4440                 ++digit;
4441                 _nc_SPRINTF(at_page, _nc_SLIMIT(sizeof(at_page)) "%02x", digit);
4442                 last_show_wacs = 0;
4443             } else if (c == '-' && digit > 0) {
4444                 --digit;
4445                 _nc_SPRINTF(at_page, _nc_SLIMIT(sizeof(at_page)) "%02x",
4446                             UChar(digit));
4447                 last_show_wacs = 0;
4448             } else if (c == '>' && repeat < (COLS / 4)) {
4449                 ++repeat;
4450             } else if (c == '<' && repeat > 1) {
4451                 --repeat;
4452             } else if (c == '_') {
4453                 space = (space == ' ') ? '_' : ' ';
4454                 last_show_wacs = 0;
4455             } else if (cycle_w_attr(c, &at_code, &attr, my_list, my_size)
4456                        || cycle_colors(c, &fg, &bg, &pair)) {
4457                 if (last_show_wacs != 0)
4458                     break;
4459             } else {
4460                 beep();
4461                 break;
4462             }
4463             break;
4464         }
4465         if (pagesize != 32) {
4466             show_paged_widechars(digit, pagesize, repeat, space, attr, pair);
4467         } else if (last_show_wacs != 0) {
4468             last_show_wacs(repeat, attr, pair);
4469         } else {
4470             show_upper_widechars(digit * 32 + 128, repeat, space, attr, pair);
4471         }
4472
4473         MvPrintw(LINES - 4, 0,
4474                  "Select: a/d/t WACS, w=%d/page, @",
4475                  pagesize);
4476         printw("%s",
4477                pending_code ? at_page : "page");
4478         addstr(", x=box, u UTF-8, ^L repaint");
4479         MvPrintw(LINES - 3, 2,
4480                  "0-9,+/- non-ASCII, </> repeat, _ space, ESC=quit");
4481         if (UseColors) {
4482             MvPrintw(LINES - 2, 2,
4483                      "v/V, f/F, b/B cycle through video attributes (%s) and color %d/%d.",
4484                      my_list[at_code].name,
4485                      fg, bg);
4486         } else {
4487             MvPrintw(LINES - 2, 2,
4488                      "v/V cycles through video attributes (%s).",
4489                      my_list[at_code].name);
4490         }
4491         refresh();
4492     } while (!isQuit(c = Getchar(), TRUE));
4493
4494     Pause();
4495     erase();
4496     endwin();
4497     return OK;
4498 }
4499
4500 #endif
4501
4502 /*
4503  * Graphic-rendition test (adapted from vttest)
4504  */
4505 static int
4506 sgr_attr_test(bool recur GCC_UNUSED)
4507 {
4508     int pass;
4509
4510     for (pass = 0; pass < 2; pass++) {
4511         chtype normal = ((pass == 0 ? A_NORMAL : A_REVERSE)) | BLANK;
4512
4513         /* Use non-default colors if possible to exercise bce a little */
4514         if (UseColors) {
4515             init_pair(1, COLOR_WHITE, COLOR_BLUE);
4516             normal |= (chtype) COLOR_PAIR(1);
4517         }
4518         bkgdset(normal);
4519         erase();
4520         MvPrintw(1, 20, "Graphic rendition test pattern:");
4521
4522         MvPrintw(4, 1, "vanilla");
4523
4524 #define set_sgr(mask) bkgdset((normal^(mask)));
4525         set_sgr(A_BOLD);
4526         MvPrintw(4, 40, "bold");
4527
4528         set_sgr(A_UNDERLINE);
4529         MvPrintw(6, 6, "underline");
4530
4531         set_sgr(A_BOLD | A_UNDERLINE);
4532         MvPrintw(6, 45, "bold underline");
4533
4534         set_sgr(A_BLINK);
4535         MvPrintw(8, 1, "blink");
4536
4537         set_sgr(A_BLINK | A_BOLD);
4538         MvPrintw(8, 40, "bold blink");
4539
4540         set_sgr(A_UNDERLINE | A_BLINK);
4541         MvPrintw(10, 6, "underline blink");
4542
4543         set_sgr(A_BOLD | A_UNDERLINE | A_BLINK);
4544         MvPrintw(10, 45, "bold underline blink");
4545
4546         set_sgr(A_REVERSE);
4547         MvPrintw(12, 1, "negative");
4548
4549         set_sgr(A_BOLD | A_REVERSE);
4550         MvPrintw(12, 40, "bold negative");
4551
4552         set_sgr(A_UNDERLINE | A_REVERSE);
4553         MvPrintw(14, 6, "underline negative");
4554
4555         set_sgr(A_BOLD | A_UNDERLINE | A_REVERSE);
4556         MvPrintw(14, 45, "bold underline negative");
4557
4558         set_sgr(A_BLINK | A_REVERSE);
4559         MvPrintw(16, 1, "blink negative");
4560
4561         set_sgr(A_BOLD | A_BLINK | A_REVERSE);
4562         MvPrintw(16, 40, "bold blink negative");
4563
4564         set_sgr(A_UNDERLINE | A_BLINK | A_REVERSE);
4565         MvPrintw(18, 6, "underline blink negative");
4566
4567         set_sgr(A_BOLD | A_UNDERLINE | A_BLINK | A_REVERSE);
4568         MvPrintw(18, 45, "bold underline blink negative");
4569
4570         bkgdset(normal);
4571         MvPrintw(LINES - 2, 1, "%s background. ", pass == 0 ? "Dark" :
4572                  "Light");
4573         clrtoeol();
4574         Pause();
4575     }
4576
4577     bkgdset(A_NORMAL | BLANK);
4578     erase();
4579     endwin();
4580     return OK;
4581 }
4582
4583 /****************************************************************************
4584  *
4585  * Windows and scrolling tester.
4586  *
4587  ****************************************************************************/
4588
4589 #define BOTLINES        4       /* number of line stolen from screen bottom */
4590
4591 typedef struct {
4592     int y, x;
4593 } pair;
4594
4595 #define FRAME struct frame
4596 FRAME
4597 {
4598     FRAME *next, *last;
4599     bool do_scroll;
4600     bool do_keypad;
4601     WINDOW *wind;
4602 };
4603
4604 #if defined(NCURSES_VERSION) && NCURSES_EXT_FUNCS
4605 #if (NCURSES_VERSION_PATCH < 20070331)
4606 #define is_keypad(win)   (win)->_use_keypad
4607 #define is_scrollok(win) (win)->_scroll
4608 #endif
4609 #else
4610 #define is_keypad(win)   FALSE
4611 #define is_scrollok(win) FALSE
4612 #endif
4613
4614 static WINDOW *
4615 frame_win(FRAME * curp)
4616 {
4617     return (curp != 0) ? curp->wind : stdscr;
4618 }
4619
4620 /* We need to know if these flags are actually set, so don't look in FRAME.
4621  * These names are known to work with SVr4 curses as well as ncurses.  The
4622  * _use_keypad name does not work with Solaris 8.
4623  */
4624 static bool
4625 HaveKeypad(FRAME * curp)
4626 {
4627     WINDOW *win = frame_win(curp);
4628     (void) win;
4629     return is_keypad(win);
4630 }
4631
4632 static bool
4633 HaveScroll(FRAME * curp)
4634 {
4635     WINDOW *win = frame_win(curp);
4636     (void) win;
4637     return is_scrollok(win);
4638 }
4639
4640 static void
4641 newwin_legend(FRAME * curp)
4642 {
4643 #define DATA(num, name) { name, num }
4644     static const struct {
4645         const char *msg;
4646         int code;
4647     } legend[] = {
4648         DATA(0, "^C = create window"),
4649             DATA(0, "^N = next window"),
4650             DATA(0, "^P = previous window"),
4651             DATA(0, "^F = scroll forward"),
4652             DATA(0, "^B = scroll backward"),
4653             DATA(1, "^K = keypad(%s)"),
4654             DATA(2, "^S = scrollok(%s)"),
4655             DATA(0, "^W = save window"),
4656             DATA(0, "^R = restore window"),
4657 #if HAVE_WRESIZE
4658             DATA(0, "^X = resize"),
4659 #endif
4660             DATA(3, "^Q%s = exit")
4661     };
4662 #undef DATA
4663     size_t n;
4664     bool do_keypad = HaveKeypad(curp);
4665     bool do_scroll = HaveScroll(curp);
4666     char buf[BUFSIZ];
4667
4668     move(LINES - 4, 0);
4669
4670     for (n = 0; n < SIZEOF(legend); n++) {
4671         int x;
4672
4673         switch (legend[n].code) {
4674         default:
4675             _nc_STRCPY(buf, legend[n].msg, sizeof(buf));
4676             break;
4677         case 1:
4678             _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf))
4679                         legend[n].msg, do_keypad ? "yes" : "no");
4680             break;
4681         case 2:
4682             _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf))
4683                         legend[n].msg, do_scroll ? "yes" : "no");
4684             break;
4685         case 3:
4686             _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf))
4687                         legend[n].msg, do_keypad ? "/ESC" : "");
4688             break;
4689         }
4690         x = getcurx(stdscr);
4691         addstr((COLS < (x + 3 + (int) strlen(buf))) ? "\n" : (n ? ", " : ""));
4692         addstr(buf);
4693     }
4694     clrtoeol();
4695 }
4696
4697 static void
4698 transient(FRAME * curp, NCURSES_CONST char *msg)
4699 {
4700     newwin_legend(curp);
4701     if (msg) {
4702         MvAddStr(LINES - 1, 0, msg);
4703         refresh();
4704         napms(1000);
4705     }
4706
4707     move(LINES - 1, 0);
4708     printw("%s characters are echoed, window should %sscroll.",
4709            HaveKeypad(curp) ? "Non-arrow" : "All other",
4710            HaveScroll(curp) ? "" : "not ");
4711     clrtoeol();
4712 }
4713
4714 static void
4715 newwin_report(FRAME * curp)
4716 /* report on the cursor's current position, then restore it */
4717 {
4718     WINDOW *win = frame_win(curp);
4719     int y, x;
4720
4721     if (win != stdscr)
4722         transient(curp, (char *) 0);
4723     getyx(win, y, x);
4724     move(LINES - 1, COLS - 17);
4725     printw("Y = %2d X = %2d", y, x);
4726     if (win != stdscr)
4727         refresh();
4728     else
4729         wmove(win, y, x);
4730 }
4731
4732 static pair *
4733 selectcell(int uli, int ulj, int lri, int lrj)
4734 /* arrows keys move cursor, return location at current on non-arrow key */
4735 {
4736     static pair res;            /* result cell */
4737     int si = lri - uli + 1;     /* depth of the select area */
4738     int sj = lrj - ulj + 1;     /* width of the select area */
4739     int i = 0, j = 0;           /* offsets into the select area */
4740
4741     res.y = uli;
4742     res.x = ulj;
4743     for (;;) {
4744         move(uli + i, ulj + j);
4745         newwin_report((FRAME *) 0);
4746
4747         switch (Getchar()) {
4748         case KEY_UP:
4749             i += si - 1;
4750             break;
4751         case KEY_DOWN:
4752             i++;
4753             break;
4754         case KEY_LEFT:
4755             j += sj - 1;
4756             break;
4757         case KEY_RIGHT:
4758             j++;
4759             break;
4760         case case_QUIT:
4761             return ((pair *) 0);
4762 #ifdef NCURSES_MOUSE_VERSION
4763         case KEY_MOUSE:
4764             {
4765                 MEVENT event;
4766
4767                 getmouse(&event);
4768                 if (event.y > uli && event.x > ulj) {
4769                     i = event.y - uli;
4770                     j = event.x - ulj;
4771                 } else {
4772                     beep();
4773                     break;
4774                 }
4775             }
4776 #endif
4777             /* FALLTHRU */
4778         default:
4779             res.y = uli + i;
4780             res.x = ulj + j;
4781             return (&res);
4782         }
4783         i %= si;
4784         j %= sj;
4785     }
4786 }
4787
4788 static void
4789 outerbox(pair ul, pair lr, bool onoff)
4790 /* draw or erase a box *outside* the given pair of corners */
4791 {
4792     MvAddCh(ul.y - 1, lr.x - 1, onoff ? ACS_ULCORNER : ' ');
4793     MvAddCh(ul.y - 1, lr.x + 1, onoff ? ACS_URCORNER : ' ');
4794     MvAddCh(lr.y + 1, lr.x + 1, onoff ? ACS_LRCORNER : ' ');
4795     MvAddCh(lr.y + 1, ul.x - 1, onoff ? ACS_LLCORNER : ' ');
4796     move(ul.y - 1, ul.x);
4797     hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
4798     move(ul.y, ul.x - 1);
4799     vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
4800     move(lr.y + 1, ul.x);
4801     hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
4802     move(ul.y, lr.x + 1);
4803     vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
4804 }
4805
4806 static WINDOW *
4807 getwindow(void)
4808 /* Ask user for a window definition */
4809 {
4810     WINDOW *rwindow;
4811     pair ul, lr, *tmp;
4812
4813     move(0, 0);
4814     clrtoeol();
4815     addstr("Use arrows to move cursor, anything else to mark corner 1");
4816     refresh();
4817     if ((tmp = selectcell(2, 1, LINES - BOTLINES - 2, COLS - 2)) == (pair *) 0)
4818         return ((WINDOW *) 0);
4819     memcpy(&ul, tmp, sizeof(pair));
4820     MvAddCh(ul.y - 1, ul.x - 1, ACS_ULCORNER);
4821     move(0, 0);
4822     clrtoeol();
4823     addstr("Use arrows to move cursor, anything else to mark corner 2");
4824     refresh();
4825     if ((tmp = selectcell(ul.y, ul.x, LINES - BOTLINES - 2, COLS - 2)) ==
4826         (pair *) 0)
4827         return ((WINDOW *) 0);
4828     memcpy(&lr, tmp, sizeof(pair));
4829
4830     rwindow = subwin(stdscr, lr.y - ul.y + 1, lr.x - ul.x + 1, ul.y, ul.x);
4831
4832     outerbox(ul, lr, TRUE);
4833     refresh();
4834
4835     if (rwindow != 0)
4836         wrefresh(rwindow);
4837
4838     move(0, 0);
4839     clrtoeol();
4840     return (rwindow);
4841 }
4842
4843 static void
4844 newwin_move(FRAME * curp, int dy, int dx)
4845 {
4846     WINDOW *win = frame_win(curp);
4847     int cur_y, cur_x;
4848     int max_y, max_x;
4849
4850     getyx(win, cur_y, cur_x);
4851     getmaxyx(win, max_y, max_x);
4852     if ((cur_x += dx) < 0)
4853         cur_x = 0;
4854     else if (cur_x >= max_x)
4855         cur_x = max_x - 1;
4856     if ((cur_y += dy) < 0)
4857         cur_y = 0;
4858     else if (cur_y >= max_y)
4859         cur_y = max_y - 1;
4860     wmove(win, cur_y, cur_x);
4861 }
4862
4863 static FRAME *
4864 delete_framed(FRAME * fp, bool showit)
4865 {
4866     FRAME *np = 0;
4867
4868     if (fp != 0) {
4869         fp->last->next = fp->next;
4870         fp->next->last = fp->last;
4871
4872         if (showit) {
4873             werase(fp->wind);
4874             wrefresh(fp->wind);
4875         }
4876         delwin(fp->wind);
4877
4878         np = (fp == fp->next) ? NULL : fp->next;
4879         free(fp);
4880     }
4881     return np;
4882 }
4883
4884 static int
4885 scroll_test(bool recur GCC_UNUSED)
4886 /* Demonstrate windows */
4887 {
4888     int c;
4889     FRAME *current = (FRAME *) 0, *neww;
4890     WINDOW *usescr;
4891 #if HAVE_PUTWIN && HAVE_GETWIN
4892     FILE *fp;
4893 #endif
4894
4895 #define DUMPFILE        "screendump"
4896
4897 #ifdef NCURSES_MOUSE_VERSION
4898     mousemask(BUTTON1_CLICKED, (mmask_t *) 0);
4899 #endif
4900     c = CTRL('C');
4901     raw();
4902     do {
4903         transient((FRAME *) 0, (char *) 0);
4904         switch (c) {
4905         case CTRL('C'):
4906             if ((neww = typeCalloc(FRAME, (size_t) 1)) == 0) {
4907                 failed("scroll_test");
4908                 goto breakout;
4909             }
4910             if ((neww->wind = getwindow()) == (WINDOW *) 0) {
4911                 failed("scroll_test");
4912                 free(neww);
4913                 goto breakout;
4914             }
4915
4916             if (current == 0) { /* First element,  */
4917                 neww->next = neww;      /*   so point it at itself */
4918                 neww->last = neww;
4919             } else {
4920                 neww->next = current->next;
4921                 neww->last = current;
4922                 neww->last->next = neww;
4923                 neww->next->last = neww;
4924             }
4925             current = neww;
4926             /* SVr4 curses sets the keypad on all newly-created windows to
4927              * false.  Someone reported that PDCurses makes new windows inherit
4928              * this flag.  Remove the following 'keypad()' call to test this
4929              */
4930             keypad(current->wind, TRUE);
4931             current->do_keypad = HaveKeypad(current);
4932             current->do_scroll = HaveScroll(current);
4933             break;
4934
4935         case CTRL('N'): /* go to next window */
4936             if (current)
4937                 current = current->next;
4938             break;
4939
4940         case CTRL('P'): /* go to previous window */
4941             if (current)
4942                 current = current->last;
4943             break;
4944
4945         case CTRL('F'): /* scroll current window forward */
4946             if (current)
4947                 wscrl(frame_win(current), 1);
4948             break;
4949
4950         case CTRL('B'): /* scroll current window backwards */
4951             if (current)
4952                 wscrl(frame_win(current), -1);
4953             break;
4954
4955         case CTRL('K'): /* toggle keypad mode for current */
4956             if (current) {
4957                 current->do_keypad = !current->do_keypad;
4958                 keypad(current->wind, current->do_keypad);
4959             }
4960             break;
4961
4962         case CTRL('S'):
4963             if (current) {
4964                 current->do_scroll = !current->do_scroll;
4965                 scrollok(current->wind, current->do_scroll);
4966             }
4967             break;
4968
4969 #if HAVE_PUTWIN && HAVE_GETWIN
4970         case CTRL('W'): /* save and delete window */
4971             if ((current != 0) && (current == current->next)) {
4972                 transient(current, "Will not save/delete ONLY window");
4973                 break;
4974             } else if ((fp = fopen(DUMPFILE, "w")) == (FILE *) 0) {
4975                 transient(current, "Can't open screen dump file");
4976             } else {
4977                 int rc = putwin(frame_win(current), fp);
4978                 (void) fclose(fp);
4979
4980                 if (rc == OK) {
4981                     current = delete_framed(current, TRUE);
4982                 } else {
4983                     transient(current, "Can't write screen dump file");
4984                 }
4985             }
4986             break;
4987
4988         case CTRL('R'): /* restore window */
4989             if ((fp = fopen(DUMPFILE, "r")) == (FILE *) 0) {
4990                 transient(current, "Can't open screen dump file");
4991             } else {
4992                 if ((neww = typeCalloc(FRAME, (size_t) 1)) != 0) {
4993
4994                     neww->next = current ? current->next : 0;
4995                     neww->last = current;
4996                     if (neww->last != 0)
4997                         neww->last->next = neww;
4998                     if (neww->next != 0)
4999                         neww->next->last = neww;
5000
5001                     neww->wind = getwin(fp);
5002
5003                     wrefresh(neww->wind);
5004                 } else {
5005                     failed("scroll_test");
5006                 }
5007                 (void) fclose(fp);
5008             }
5009             break;
5010 #endif
5011
5012 #if HAVE_WRESIZE
5013         case CTRL('X'): /* resize window */
5014             if (current) {
5015                 pair *tmp, ul, lr;
5016                 int mx, my;
5017
5018                 move(0, 0);
5019                 clrtoeol();
5020                 addstr("Use arrows to move cursor, anything else to mark new corner");
5021                 refresh();
5022
5023                 getbegyx(current->wind, ul.y, ul.x);
5024
5025                 tmp = selectcell(ul.y, ul.x, LINES - BOTLINES - 2, COLS - 2);
5026                 if (tmp == (pair *) 0) {
5027                     beep();
5028                     break;
5029                 }
5030
5031                 getmaxyx(current->wind, lr.y, lr.x);
5032                 lr.y += (ul.y - 1);
5033                 lr.x += (ul.x - 1);
5034                 outerbox(ul, lr, FALSE);
5035                 wnoutrefresh(stdscr);
5036
5037                 /* strictly cosmetic hack for the test */
5038                 getmaxyx(current->wind, my, mx);
5039                 if (my > tmp->y - ul.y) {
5040                     getyx(current->wind, lr.y, lr.x);
5041                     wmove(current->wind, tmp->y - ul.y + 1, 0);
5042                     wclrtobot(current->wind);
5043                     wmove(current->wind, lr.y, lr.x);
5044                 }
5045                 if (mx > tmp->x - ul.x) {
5046                     int i;
5047                     for (i = 0; i < my; i++) {
5048                         wmove(current->wind, i, tmp->x - ul.x + 1);
5049                         wclrtoeol(current->wind);
5050                     }
5051                 }
5052                 wnoutrefresh(current->wind);
5053
5054                 memcpy(&lr, tmp, sizeof(pair));
5055                 (void) wresize(current->wind, lr.y - ul.y + 0, lr.x - ul.x + 0);
5056
5057                 getbegyx(current->wind, ul.y, ul.x);
5058                 getmaxyx(current->wind, lr.y, lr.x);
5059                 lr.y += (ul.y - 1);
5060                 lr.x += (ul.x - 1);
5061                 outerbox(ul, lr, TRUE);
5062                 wnoutrefresh(stdscr);
5063
5064                 wnoutrefresh(current->wind);
5065                 move(0, 0);
5066                 clrtoeol();
5067                 doupdate();
5068             }
5069             break;
5070 #endif /* HAVE_WRESIZE */
5071
5072         case KEY_UP:
5073             newwin_move(current, -1, 0);
5074             break;
5075         case KEY_DOWN:
5076             newwin_move(current, 1, 0);
5077             break;
5078         case KEY_LEFT:
5079             newwin_move(current, 0, -1);
5080             break;
5081         case KEY_RIGHT:
5082             newwin_move(current, 0, 1);
5083             break;
5084
5085         case KEY_BACKSPACE:
5086             /* FALLTHROUGH */
5087         case KEY_DC:
5088             {
5089                 int y, x;
5090                 getyx(frame_win(current), y, x);
5091                 if (--x < 0) {
5092                     if (--y < 0)
5093                         break;
5094                     x = getmaxx(frame_win(current)) - 1;
5095                 }
5096                 (void) mvwdelch(frame_win(current), y, x);
5097             }
5098             break;
5099
5100         case '\r':
5101             c = '\n';
5102             /* FALLTHROUGH */
5103
5104         default:
5105             if (current)
5106                 waddch(current->wind, (chtype) c);
5107             else
5108                 beep();
5109             break;
5110         }
5111         newwin_report(current);
5112         usescr = frame_win(current);
5113         wrefresh(usescr);
5114     } while
5115         (!isQuit(c = wGetchar(usescr), TRUE)
5116          && (c != ERR));
5117
5118   breakout:
5119     while (current != 0)
5120         current = delete_framed(current, FALSE);
5121
5122     scrollok(stdscr, TRUE);     /* reset to driver's default */
5123 #ifdef NCURSES_MOUSE_VERSION
5124     mousemask(0, (mmask_t *) 0);
5125 #endif
5126     noraw();
5127     erase();
5128     endwin();
5129     return OK;
5130 }
5131
5132 /****************************************************************************
5133  *
5134  * Panels tester
5135  *
5136  ****************************************************************************/
5137
5138 #if USE_LIBPANEL
5139 static int nap_msec = 1;
5140
5141 static NCURSES_CONST char *mod[] =
5142 {
5143     "test ",
5144     "TEST ",
5145     "(**) ",
5146     "*()* ",
5147     "<--> ",
5148     "LAST "
5149 };
5150
5151 /*+-------------------------------------------------------------------------
5152         wait_a_while(msec)
5153 --------------------------------------------------------------------------*/
5154 static void
5155 wait_a_while(int msec GCC_UNUSED)
5156 {
5157 #if HAVE_NAPMS
5158     if (nap_msec == 1)
5159         wGetchar(stdscr);
5160     else
5161         napms(nap_msec);
5162 #else
5163     if (nap_msec == 1)
5164         wGetchar(stdscr);
5165     else if (msec > 1000)
5166         sleep((unsigned) msec / 1000);
5167     else
5168         sleep(1);
5169 #endif
5170 }                               /* end of wait_a_while */
5171
5172 /*+-------------------------------------------------------------------------
5173         saywhat(text)
5174 --------------------------------------------------------------------------*/
5175 static void
5176 saywhat(NCURSES_CONST char *text)
5177 {
5178     wmove(stdscr, LINES - 1, 0);
5179     wclrtoeol(stdscr);
5180     if (text != 0 && *text != '\0') {
5181         waddstr(stdscr, text);
5182         waddstr(stdscr, "; ");
5183     }
5184     waddstr(stdscr, "press any key to continue");
5185 }                               /* end of saywhat */
5186
5187 /*+-------------------------------------------------------------------------
5188         mkpanel(rows,cols,tly,tlx) - alloc a win and panel and associate them
5189 --------------------------------------------------------------------------*/
5190 static PANEL *
5191 mkpanel(NCURSES_COLOR_T color, int rows, int cols, int tly, int tlx)
5192 {
5193     WINDOW *win;
5194     PANEL *pan = 0;
5195
5196     if ((win = newwin(rows, cols, tly, tlx)) != 0) {
5197         if ((pan = new_panel(win)) == 0) {
5198             delwin(win);
5199         } else if (UseColors) {
5200             NCURSES_COLOR_T fg = (NCURSES_COLOR_T) ((color == COLOR_BLUE)
5201                                                     ? COLOR_WHITE
5202                                                     : COLOR_BLACK);
5203             NCURSES_COLOR_T bg = color;
5204
5205             init_pair(color, fg, bg);
5206             wbkgdset(win, (attr_t) (COLOR_PAIR(color) | ' '));
5207         } else {
5208             wbkgdset(win, A_BOLD | ' ');
5209         }
5210     }
5211     return pan;
5212 }                               /* end of mkpanel */
5213
5214 /*+-------------------------------------------------------------------------
5215         rmpanel(pan)
5216 --------------------------------------------------------------------------*/
5217 static void
5218 rmpanel(PANEL *pan)
5219 {
5220     WINDOW *win = panel_window(pan);
5221     del_panel(pan);
5222     delwin(win);
5223 }                               /* end of rmpanel */
5224
5225 /*+-------------------------------------------------------------------------
5226         pflush()
5227 --------------------------------------------------------------------------*/
5228 static void
5229 pflush(void)
5230 {
5231     update_panels();
5232     doupdate();
5233 }                               /* end of pflush */
5234
5235 /*+-------------------------------------------------------------------------
5236         fill_panel(win)
5237 --------------------------------------------------------------------------*/
5238 static void
5239 init_panel(WINDOW *win)
5240 {
5241     register int y, x;
5242
5243     for (y = 0; y < LINES - 1; y++) {
5244         for (x = 0; x < COLS; x++)
5245             wprintw(win, "%d", (y + x) % 10);
5246     }
5247 }
5248
5249 static void
5250 fill_panel(PANEL *pan)
5251 {
5252     WINDOW *win = panel_window(pan);
5253     const char *userptr = (const char *) panel_userptr(pan);
5254     int num = (userptr && *userptr) ? userptr[1] : '?';
5255     int y, x;
5256
5257     wmove(win, 1, 1);
5258     wprintw(win, "-pan%c-", num);
5259     wclrtoeol(win);
5260     box(win, 0, 0);
5261     for (y = 2; y < getmaxy(win) - 1; y++) {
5262         for (x = 1; x < getmaxx(win) - 1; x++) {
5263             wmove(win, y, x);
5264             waddch(win, UChar(num));
5265         }
5266     }
5267 }
5268
5269 #if USE_WIDEC_SUPPORT
5270 static void
5271 init_wide_panel(WINDOW *win)
5272 {
5273     int digit;
5274     cchar_t temp[10];
5275
5276     for (digit = 0; digit < 10; ++digit)
5277         make_fullwidth_digit(&temp[digit], digit);
5278
5279     do {
5280         int y, x;
5281         getyx(stdscr, y, x);
5282         digit = (y + x / 2) % 10;
5283     } while (wadd_wch(win, &temp[digit]) != ERR);
5284 }
5285
5286 static void
5287 fill_wide_panel(PANEL *pan)
5288 {
5289     WINDOW *win = panel_window(pan);
5290     const char *userptr = (const char *) panel_userptr(pan);
5291     int num = (userptr && *userptr) ? userptr[1] : '?';
5292     int y, x;
5293
5294     wmove(win, 1, 1);
5295     wprintw(win, "-pan%c-", num);
5296     wclrtoeol(win);
5297     box(win, 0, 0);
5298     for (y = 2; y < getmaxy(win) - 1; y++) {
5299         for (x = 1; x < getmaxx(win) - 1; x++) {
5300             wmove(win, y, x);
5301             waddch(win, UChar(num));
5302         }
5303     }
5304 }
5305 #endif
5306
5307 #define MAX_PANELS 5
5308
5309 static void
5310 canned_panel(PANEL *px[MAX_PANELS + 1], NCURSES_CONST char *cmd)
5311 {
5312     int which = cmd[1] - '0';
5313
5314     saywhat(cmd);
5315     switch (*cmd) {
5316     case 'h':
5317         hide_panel(px[which]);
5318         break;
5319     case 's':
5320         show_panel(px[which]);
5321         break;
5322     case 't':
5323         top_panel(px[which]);
5324         break;
5325     case 'b':
5326         bottom_panel(px[which]);
5327         break;
5328     case 'd':
5329         rmpanel(px[which]);
5330         break;
5331     }
5332     pflush();
5333     wait_a_while(nap_msec);
5334 }
5335
5336 static int
5337 demo_panels(void (*InitPanel) (WINDOW *), void (*FillPanel) (PANEL *))
5338 {
5339     int count;
5340     int itmp;
5341     PANEL *px[MAX_PANELS + 1];
5342
5343     scrollok(stdscr, FALSE);    /* we don't want stdscr to scroll! */
5344     refresh();
5345
5346     InitPanel(stdscr);
5347     for (count = 0; count < 5; count++) {
5348         px[1] = mkpanel(COLOR_RED,
5349                         LINES / 2 - 2,
5350                         COLS / 8 + 1,
5351                         0,
5352                         0);
5353         set_panel_userptr(px[1], (NCURSES_CONST void *) "p1");
5354
5355         px[2] = mkpanel(COLOR_GREEN,
5356                         LINES / 2 + 1,
5357                         COLS / 7,
5358                         LINES / 4,
5359                         COLS / 10);
5360         set_panel_userptr(px[2], (NCURSES_CONST void *) "p2");
5361
5362         px[3] = mkpanel(COLOR_YELLOW,
5363                         LINES / 4,
5364                         COLS / 10,
5365                         LINES / 2,
5366                         COLS / 9);
5367         set_panel_userptr(px[3], (NCURSES_CONST void *) "p3");
5368
5369         px[4] = mkpanel(COLOR_BLUE,
5370                         LINES / 2 - 2,
5371                         COLS / 8,
5372                         LINES / 2 - 2,
5373                         COLS / 3);
5374         set_panel_userptr(px[4], (NCURSES_CONST void *) "p4");
5375
5376         px[5] = mkpanel(COLOR_MAGENTA,
5377                         LINES / 2 - 2,
5378                         COLS / 8,
5379                         LINES / 2,
5380                         COLS / 2 - 2);
5381         set_panel_userptr(px[5], (NCURSES_CONST void *) "p5");
5382
5383         FillPanel(px[1]);
5384         FillPanel(px[2]);
5385         FillPanel(px[3]);
5386         FillPanel(px[4]);
5387         FillPanel(px[5]);
5388
5389         hide_panel(px[4]);
5390         hide_panel(px[5]);
5391         pflush();
5392         saywhat("");
5393         wait_a_while(nap_msec);
5394
5395         saywhat("h3 s1 s2 s4 s5");
5396         move_panel(px[1], 0, 0);
5397         hide_panel(px[3]);
5398         show_panel(px[1]);
5399         show_panel(px[2]);
5400         show_panel(px[4]);
5401         show_panel(px[5]);
5402         pflush();
5403         wait_a_while(nap_msec);
5404
5405         canned_panel(px, "s1");
5406         canned_panel(px, "s2");
5407
5408         saywhat("m2");
5409         move_panel(px[2], LINES / 3 + 1, COLS / 8);
5410         pflush();
5411         wait_a_while(nap_msec);
5412
5413         canned_panel(px, "s3");
5414
5415         saywhat("m3");
5416         move_panel(px[3], LINES / 4 + 1, COLS / 15);
5417         pflush();
5418         wait_a_while(nap_msec);
5419
5420         canned_panel(px, "b3");
5421         canned_panel(px, "s4");
5422         canned_panel(px, "s5");
5423         canned_panel(px, "t3");
5424         canned_panel(px, "t1");
5425         canned_panel(px, "t2");
5426         canned_panel(px, "t3");
5427         canned_panel(px, "t4");
5428
5429         for (itmp = 0; itmp < 6; itmp++) {
5430             WINDOW *w4 = panel_window(px[4]);
5431             WINDOW *w5 = panel_window(px[5]);
5432
5433             saywhat("m4");
5434             wmove(w4, LINES / 8, 1);
5435             waddstr(w4, mod[itmp]);
5436             move_panel(px[4], LINES / 6, itmp * (COLS / 8));
5437             wmove(w5, LINES / 6, 1);
5438             waddstr(w5, mod[itmp]);
5439             pflush();
5440             wait_a_while(nap_msec);
5441
5442             saywhat("m5");
5443             wmove(w4, LINES / 6, 1);
5444             waddstr(w4, mod[itmp]);
5445             move_panel(px[5], LINES / 3 - 1, (itmp * 10) + 6);
5446             wmove(w5, LINES / 8, 1);
5447             waddstr(w5, mod[itmp]);
5448             pflush();
5449             wait_a_while(nap_msec);
5450         }
5451
5452         saywhat("m4");
5453         move_panel(px[4], LINES / 6, itmp * (COLS / 8));
5454         pflush();
5455         wait_a_while(nap_msec);
5456
5457         canned_panel(px, "t5");
5458         canned_panel(px, "t2");
5459         canned_panel(px, "t1");
5460         canned_panel(px, "d2");
5461         canned_panel(px, "h3");
5462         canned_panel(px, "d1");
5463         canned_panel(px, "d4");
5464         canned_panel(px, "d5");
5465         canned_panel(px, "d3");
5466
5467         wait_a_while(nap_msec);
5468         if (nap_msec == 1)
5469             break;
5470         nap_msec = 100L;
5471     }
5472
5473     erase();
5474     endwin();
5475     return OK;
5476 }
5477
5478 #if USE_LIBPANEL
5479 static int
5480 panel_test(bool recur GCC_UNUSED)
5481 {
5482     return demo_panels(init_panel, fill_panel);
5483 }
5484 #endif
5485
5486 #if USE_WIDEC_SUPPORT && USE_LIBPANEL
5487 static int
5488 x_panel_test(bool recur GCC_UNUSED)
5489 {
5490     return demo_panels(init_wide_panel, fill_wide_panel);
5491 }
5492 #endif
5493 #endif /* USE_LIBPANEL */
5494
5495 /****************************************************************************
5496  *
5497  * Pad tester
5498  *
5499  ****************************************************************************/
5500
5501 #if HAVE_NEWPAD
5502
5503 /* The behavior of mvhline, mvvline for negative/zero length is unspecified,
5504  * though we can rely on negative x/y values to stop the macro.
5505  */
5506 static void
5507 do_h_line(int y, int x, chtype c, int to)
5508 {
5509     if ((to) > (x))
5510         MvHLine(y, x, c, (to) - (x));
5511 }
5512
5513 static void
5514 do_v_line(int y, int x, chtype c, int to)
5515 {
5516     if ((to) > (y))
5517         MvVLine(y, x, c, (to) - (y));
5518 }
5519
5520 #define GRIDSIZE        3
5521
5522 static bool pending_pan = FALSE;
5523 static bool show_panner_legend = TRUE;
5524
5525 static int
5526 panner_legend(int line)
5527 {
5528     static const char *const legend[] =
5529     {
5530         "Use arrow keys (or U,D,L,R) to pan, ESC to quit, ! to shell-out.",
5531         "Use +,- (or j,k) to grow/shrink the panner vertically.",
5532         "Use <,> (or h,l) to grow/shrink the panner horizontally.",
5533         "Number repeats.  Toggle legend:? filler:a timer:t scrollmark:s."
5534     };
5535     int n = ((int) SIZEOF(legend) - (LINES - line));
5536     if (n >= 0) {
5537         if (move(line, 0) != ERR) {
5538             if (show_panner_legend)
5539                 printw("%s", legend[n]);
5540             clrtoeol();
5541             return show_panner_legend;
5542         }
5543     }
5544     return FALSE;
5545 }
5546
5547 static void
5548 panner_h_cleanup(int from_y, int from_x, int to_x)
5549 {
5550     if (!panner_legend(from_y))
5551         do_h_line(from_y, from_x, ' ', to_x);
5552 }
5553
5554 static void
5555 panner_v_cleanup(int from_y, int from_x, int to_y)
5556 {
5557     if (!panner_legend(from_y))
5558         do_v_line(from_y, from_x, ' ', to_y);
5559 }
5560
5561 static void
5562 fill_pad(WINDOW *panpad, bool pan_lines, bool colored)
5563 {
5564     int y, x;
5565     unsigned gridcount = 0;
5566     chtype fill = 0;
5567 #ifdef A_COLOR
5568     if (colored)
5569         fill = (chtype) COLOR_PAIR(1);
5570 #endif
5571
5572     wmove(panpad, 0, 0);
5573     for (y = 0; y < getmaxy(panpad); y++) {
5574         for (x = 0; x < getmaxx(panpad); x++) {
5575             if (y % GRIDSIZE == 0 && x % GRIDSIZE == 0) {
5576                 if (y == 0 && x == 0)
5577                     waddch(panpad, pan_lines ? ACS_ULCORNER : '+');
5578                 else if (y == 0)
5579                     waddch(panpad, pan_lines ? ACS_TTEE : '+');
5580                 else if (y == 0 || x == 0)
5581                     waddch(panpad, pan_lines ? ACS_LTEE : '+');
5582                 else
5583                     waddch(panpad, (chtype) ((pan_lines ? 'a' : 'A') +
5584                                              (int) (gridcount++ % 26)) | fill);
5585             } else if (y % GRIDSIZE == 0)
5586                 waddch(panpad, pan_lines ? ACS_HLINE : '-');
5587             else if (x % GRIDSIZE == 0)
5588                 waddch(panpad, pan_lines ? ACS_VLINE : '|');
5589             else
5590                 waddch(panpad, ' ');
5591         }
5592     }
5593 }
5594
5595 static void
5596 panner(WINDOW *pad,
5597        int top_x, int top_y, int porty, int portx,
5598        int (*pgetc) (WINDOW *),
5599        bool colored)
5600 {
5601 #if HAVE_GETTIMEOFDAY
5602     struct timeval before, after;
5603     bool timing = TRUE;
5604 #endif
5605     bool pan_lines = FALSE;
5606     bool scrollers = TRUE;
5607     int basex = 0;
5608     int basey = 0;
5609     int pxmax, pymax, lowend, highend, c;
5610
5611     getmaxyx(pad, pymax, pxmax);
5612     scrollok(stdscr, FALSE);    /* we don't want stdscr to scroll! */
5613
5614     c = KEY_REFRESH;
5615     do {
5616 #ifdef NCURSES_VERSION
5617         /*
5618          * During shell-out, the user may have resized the window.  Adjust
5619          * the port size of the pad to accommodate this.  Ncurses automatically
5620          * resizes all of the normal windows to fit on the new screen.
5621          */
5622         if (top_x > COLS)
5623             top_x = COLS;
5624         if (portx > COLS)
5625             portx = COLS;
5626         if (top_y > LINES)
5627             top_y = LINES;
5628         if (porty > LINES)
5629             porty = LINES;
5630 #endif
5631         switch (c) {
5632         case KEY_REFRESH:
5633             erase();
5634
5635             /* FALLTHRU */
5636         case HELP_KEY_1:
5637             if (c == HELP_KEY_1)
5638                 show_panner_legend = !show_panner_legend;
5639             panner_legend(LINES - 4);
5640             panner_legend(LINES - 3);
5641             panner_legend(LINES - 2);
5642             panner_legend(LINES - 1);
5643             break;
5644         case 'a':
5645             pan_lines = !pan_lines;
5646             fill_pad(pad, pan_lines, colored);
5647             pending_pan = FALSE;
5648             break;
5649
5650 #if HAVE_GETTIMEOFDAY
5651         case 't':
5652             timing = !timing;
5653             if (!timing)
5654                 panner_legend(LINES - 1);
5655             break;
5656 #endif
5657         case 's':
5658             scrollers = !scrollers;
5659             break;
5660
5661             /* Move the top-left corner of the pad, keeping the bottom-right
5662              * corner fixed.
5663              */
5664         case 'h':               /* increase-columns: move left edge to left */
5665             if (top_x <= 0)
5666                 beep();
5667             else {
5668                 panner_v_cleanup(top_y, top_x, porty);
5669                 top_x--;
5670             }
5671             break;
5672
5673         case 'j':               /* decrease-lines: move top-edge down */
5674             if (top_y >= porty)
5675                 beep();
5676             else {
5677                 panner_h_cleanup(top_y - 1, top_x - (top_x > 0), portx);
5678                 top_y++;
5679             }
5680             break;
5681
5682         case 'k':               /* increase-lines: move top-edge up */
5683             if (top_y <= 0)
5684                 beep();
5685             else {
5686                 top_y--;
5687                 panner_h_cleanup(top_y, top_x, portx);
5688             }
5689             break;
5690
5691         case 'l':               /* decrease-columns: move left-edge to right */
5692             if (top_x >= portx)
5693                 beep();
5694             else {
5695                 panner_v_cleanup(top_y - (top_y > 0), top_x - 1, porty);
5696                 top_x++;
5697             }
5698             break;
5699
5700             /* Move the bottom-right corner of the pad, keeping the top-left
5701              * corner fixed.
5702              */
5703         case KEY_IC:            /* increase-columns: move right-edge to right */
5704             if (portx >= pxmax || portx >= COLS)
5705                 beep();
5706             else {
5707                 panner_v_cleanup(top_y - (top_y > 0), portx - 1, porty);
5708                 ++portx;
5709             }
5710             break;
5711
5712         case KEY_IL:            /* increase-lines: move bottom-edge down */
5713             if (porty >= pymax || porty >= LINES)
5714                 beep();
5715             else {
5716                 panner_h_cleanup(porty - 1, top_x - (top_x > 0), portx);
5717                 ++porty;
5718             }
5719             break;
5720
5721         case KEY_DC:            /* decrease-columns: move bottom edge up */
5722             if (portx <= top_x)
5723                 beep();
5724             else {
5725                 portx--;
5726                 panner_v_cleanup(top_y - (top_y > 0), portx, porty);
5727             }
5728             break;
5729
5730         case KEY_DL:            /* decrease-lines */
5731             if (porty <= top_y)
5732                 beep();
5733             else {
5734                 porty--;
5735                 panner_h_cleanup(porty, top_x - (top_x > 0), portx);
5736             }
5737             break;
5738
5739         case KEY_LEFT:          /* pan leftwards */
5740             if (basex > 0)
5741                 basex--;
5742             else
5743                 beep();
5744             break;
5745
5746         case KEY_RIGHT: /* pan rightwards */
5747             if (basex + portx - (pymax > porty) < pxmax)
5748                 basex++;
5749             else
5750                 beep();
5751             break;
5752
5753         case KEY_UP:            /* pan upwards */
5754             if (basey > 0)
5755                 basey--;
5756             else
5757                 beep();
5758             break;
5759
5760         case KEY_DOWN:          /* pan downwards */
5761             if (basey + porty - (pxmax > portx) < pymax)
5762                 basey++;
5763             else
5764                 beep();
5765             break;
5766
5767         case 'H':
5768         case KEY_HOME:
5769         case KEY_FIND:
5770             basey = 0;
5771             break;
5772
5773         case 'E':
5774         case KEY_END:
5775         case KEY_SELECT:
5776             basey = pymax - porty;
5777             if (basey < 0)
5778                 basey = 0;
5779             break;
5780
5781         default:
5782             beep();
5783             break;
5784         }
5785
5786         MvAddCh(top_y - 1, top_x - 1, ACS_ULCORNER);
5787         do_v_line(top_y, top_x - 1, ACS_VLINE, porty);
5788         do_h_line(top_y - 1, top_x, ACS_HLINE, portx);
5789
5790         if (scrollers && (pxmax > portx - 1)) {
5791             int length = (portx - top_x - 1);
5792             float ratio = ((float) length) / ((float) pxmax);
5793
5794             lowend = (int) ((float) top_x + ((float) basex * ratio));
5795             highend = (int) ((float) top_x + ((float) (basex + length) * ratio));
5796
5797             do_h_line(porty - 1, top_x, ACS_HLINE, lowend);
5798             if (highend < portx) {
5799                 attron(A_REVERSE);
5800                 do_h_line(porty - 1, lowend, ' ', highend + 1);
5801                 attroff(A_REVERSE);
5802                 do_h_line(porty - 1, highend + 1, ACS_HLINE, portx);
5803             }
5804         } else
5805             do_h_line(porty - 1, top_x, ACS_HLINE, portx);
5806
5807         if (scrollers && (pymax > porty - 1)) {
5808             int length = (porty - top_y - 1);
5809             float ratio = ((float) length) / ((float) pymax);
5810
5811             lowend = (int) ((float) top_y + ((float) basey * ratio));
5812             highend = (int) ((float) top_y + ((float) (basey + length) * ratio));
5813
5814             do_v_line(top_y, portx - 1, ACS_VLINE, lowend);
5815             if (highend < porty) {
5816                 attron(A_REVERSE);
5817                 do_v_line(lowend, portx - 1, ' ', highend + 1);
5818                 attroff(A_REVERSE);
5819                 do_v_line(highend + 1, portx - 1, ACS_VLINE, porty);
5820             }
5821         } else
5822             do_v_line(top_y, portx - 1, ACS_VLINE, porty);
5823
5824         MvAddCh(top_y - 1, portx - 1, ACS_URCORNER);
5825         MvAddCh(porty - 1, top_x - 1, ACS_LLCORNER);
5826         MvAddCh(porty - 1, portx - 1, ACS_LRCORNER);
5827
5828         if (!pending_pan) {
5829 #if HAVE_GETTIMEOFDAY
5830             gettimeofday(&before, 0);
5831 #endif
5832             wnoutrefresh(stdscr);
5833
5834             pnoutrefresh(pad,
5835                          basey, basex,
5836                          top_y, top_x,
5837                          porty - (pxmax > portx) - 1,
5838                          portx - (pymax > porty) - 1);
5839
5840             doupdate();
5841 #if HAVE_GETTIMEOFDAY
5842 #define TIMEVAL2S(data) ((double) data.tv_sec + ((double) data.tv_usec / 1.0e6))
5843             if (timing) {
5844                 double elapsed;
5845                 gettimeofday(&after, 0);
5846                 elapsed = (TIMEVAL2S(after) - TIMEVAL2S(before));
5847                 move(LINES - 1, COLS - 12);
5848                 printw("Secs: %2.03f", elapsed);
5849                 refresh();
5850             }
5851 #endif
5852         }
5853
5854     } while
5855         ((c = pgetc(pad)) != KEY_EXIT);
5856
5857     scrollok(stdscr, TRUE);     /* reset to driver's default */
5858 }
5859
5860 static int
5861 padgetch(WINDOW *win)
5862 {
5863     static int count;
5864     static int last;
5865
5866     if ((pending_pan = (count > 0)) != FALSE) {
5867         count--;
5868         pending_pan = (count != 0);
5869     } else {
5870         for (;;) {
5871             int c;
5872             switch (c = wGetchar(win)) {
5873             case '!':
5874                 ShellOut(FALSE);
5875                 /* FALLTHRU */
5876             case CTRL('r'):
5877                 endwin();
5878                 refresh();
5879                 c = KEY_REFRESH;
5880                 break;
5881             case CTRL('l'):
5882                 c = KEY_REFRESH;
5883                 break;
5884             case 'U':
5885                 c = KEY_UP;
5886                 break;
5887             case 'D':
5888                 c = KEY_DOWN;
5889                 break;
5890             case 'R':
5891                 c = KEY_RIGHT;
5892                 break;
5893             case 'L':
5894                 c = KEY_LEFT;
5895                 break;
5896             case '+':
5897                 c = KEY_IL;
5898                 break;
5899             case '-':
5900                 c = KEY_DL;
5901                 break;
5902             case '>':
5903                 c = KEY_IC;
5904                 break;
5905             case '<':
5906                 c = KEY_DC;
5907                 break;
5908             case ERR:           /* FALLTHRU */
5909             case case_QUIT:
5910                 count = 0;
5911                 c = KEY_EXIT;
5912                 break;
5913             default:
5914                 if (c >= '0' && c <= '9') {
5915                     count = count * 10 + (c - '0');
5916                     continue;
5917                 }
5918                 break;
5919             }
5920             last = c;
5921             break;
5922         }
5923         if (count > 0)
5924             count--;
5925     }
5926     return (last);
5927 }
5928
5929 #define PAD_HIGH 200
5930 #define PAD_WIDE 200
5931
5932 static int
5933 pad_test(bool recur GCC_UNUSED)
5934 /* Demonstrate pads. */
5935 {
5936     WINDOW *panpad = newpad(PAD_HIGH, PAD_WIDE);
5937
5938     if (panpad == 0) {
5939         Cannot("cannot create requested pad");
5940         return ERR;
5941     }
5942 #ifdef A_COLOR
5943     if (UseColors) {
5944         init_pair(1, COLOR_BLACK, COLOR_GREEN);
5945         init_pair(2, COLOR_CYAN, COLOR_BLUE);
5946         wbkgd(panpad, (chtype) (COLOR_PAIR(2) | ' '));
5947     }
5948 #endif
5949     fill_pad(panpad, FALSE, TRUE);
5950
5951     panner_legend(LINES - 4);
5952     panner_legend(LINES - 3);
5953     panner_legend(LINES - 2);
5954     panner_legend(LINES - 1);
5955
5956     keypad(panpad, TRUE);
5957
5958     /* Make the pad (initially) narrow enough that a trace file won't wrap.
5959      * We'll still be able to widen it during a test, since that's required
5960      * for testing boundaries.
5961      */
5962     panner(panpad, 2, 2, LINES - 5, COLS - 15, padgetch, TRUE);
5963
5964     delwin(panpad);
5965     endwin();
5966     erase();
5967     return OK;
5968 }
5969 #endif /* HAVE_NEWPAD */
5970
5971 /****************************************************************************
5972  *
5973  * Tests from John Burnell's PDCurses tester
5974  *
5975  ****************************************************************************/
5976
5977 static void
5978 Continue(WINDOW *win)
5979 {
5980     noecho();
5981     wmove(win, 10, 1);
5982     MvWAddStr(win, 10, 1, " Press any key to continue");
5983     wrefresh(win);
5984     wGetchar(win);
5985 }
5986
5987 static int
5988 flushinp_test(bool recur GCC_UNUSED)
5989 /* Input test, adapted from John Burnell's PDCurses tester */
5990 {
5991     WINDOW *win = stdscr;
5992     int w, h, bx, by, sw, sh, i;
5993
5994     WINDOW *subWin;
5995     wclear(win);
5996
5997     getmaxyx(win, h, w);
5998     getbegyx(win, by, bx);
5999     sw = w / 3;
6000     sh = h / 3;
6001     if ((subWin = subwin(win, sh, sw, by + h - sh - 2, bx + w - sw - 2)) == 0)
6002         return ERR;
6003
6004 #ifdef A_COLOR
6005     if (UseColors) {
6006         init_pair(2, COLOR_CYAN, COLOR_BLUE);
6007         wbkgd(subWin, (chtype) (COLOR_PAIR(2) | ' '));
6008     }
6009 #endif
6010     (void) wattrset(subWin, A_BOLD);
6011     box(subWin, ACS_VLINE, ACS_HLINE);
6012     MvWAddStr(subWin, 2, 1, "This is a subwindow");
6013     wrefresh(win);
6014
6015     /*
6016      * This used to set 'nocbreak()'.  However, Alexander Lukyanov says that
6017      * it only happened to "work" on SVr4 because that implementation does not
6018      * emulate nocbreak+noecho mode, whereas ncurses does.  To get the desired
6019      * test behavior, we're using 'cbreak()', which will allow a single
6020      * character to return without needing a newline. - T.Dickey 1997/10/11.
6021      */
6022     cbreak();
6023     MvWAddStr(win, 0, 1, "This is a test of the flushinp() call.");
6024
6025     MvWAddStr(win, 2, 1, "Type random keys for 5 seconds.");
6026     MvWAddStr(win, 3, 1,
6027               "These should be discarded (not echoed) after the subwindow goes away.");
6028     wrefresh(win);
6029
6030     for (i = 0; i < 5; i++) {
6031         MvWPrintw(subWin, 1, 1, "Time = %d", i);
6032         wrefresh(subWin);
6033         napms(1000);
6034         flushinp();
6035     }
6036
6037     delwin(subWin);
6038     werase(win);
6039     flash();
6040     wrefresh(win);
6041     napms(1000);
6042
6043     MvWAddStr(win, 2, 1,
6044               "If you were still typing when the window timer expired,");
6045     MvWAddStr(win, 3, 1,
6046               "or else you typed nothing at all while it was running,");
6047     MvWAddStr(win, 4, 1,
6048               "test was invalid.  You'll see garbage or nothing at all. ");
6049     MvWAddStr(win, 6, 1, "Press a key");
6050     wmove(win, 9, 10);
6051     wrefresh(win);
6052     echo();
6053     wGetchar(win);
6054     flushinp();
6055     MvWAddStr(win, 12, 0,
6056               "If you see any key other than what you typed, flushinp() is broken.");
6057     Continue(win);
6058
6059     wmove(win, 9, 10);
6060     wdelch(win);
6061     wrefresh(win);
6062     wmove(win, 12, 0);
6063     clrtoeol();
6064     waddstr(win,
6065             "What you typed should now have been deleted; if not, wdelch() failed.");
6066     Continue(win);
6067
6068     cbreak();
6069     return OK;
6070 }
6071
6072 /****************************************************************************
6073  *
6074  * Menu test
6075  *
6076  ****************************************************************************/
6077
6078 #if USE_LIBMENU
6079
6080 #define MENU_Y  8
6081 #define MENU_X  8
6082
6083 static int
6084 menu_virtualize(int c)
6085 {
6086     if (c == '\n' || c == KEY_EXIT)
6087         return (MAX_COMMAND + 1);
6088     else if (c == 'u')
6089         return (REQ_SCR_ULINE);
6090     else if (c == 'd')
6091         return (REQ_SCR_DLINE);
6092     else if (c == 'b' || c == KEY_NPAGE)
6093         return (REQ_SCR_UPAGE);
6094     else if (c == 'f' || c == KEY_PPAGE)
6095         return (REQ_SCR_DPAGE);
6096     else if (c == 'n' || c == KEY_DOWN)
6097         return (REQ_NEXT_ITEM);
6098     else if (c == 'p' || c == KEY_UP)
6099         return (REQ_PREV_ITEM);
6100     else if (c == ' ')
6101         return (REQ_TOGGLE_ITEM);
6102     else {
6103         if (c != KEY_MOUSE)
6104             beep();
6105         return (c);
6106     }
6107 }
6108
6109 static CONST_MENUS char *animals[] =
6110 {
6111     "Lions",
6112     "Tigers",
6113     "Bears",
6114     "(Oh my!)",
6115     "Newts",
6116     "Platypi",
6117     "Lemurs",
6118     "(Oh really?!)",
6119     "Leopards",
6120     "Panthers",
6121     "Pumas",
6122     "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
6123     "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs, Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
6124     (char *) 0
6125 };
6126
6127 static int
6128 menu_test(bool recur GCC_UNUSED)
6129 {
6130     MENU *m;
6131     ITEM *items[SIZEOF(animals)];
6132     ITEM **ip = items;
6133     CONST_MENUS char **ap;
6134     int mrows, mcols, c;
6135     WINDOW *menuwin;
6136
6137 #ifdef NCURSES_MOUSE_VERSION
6138     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
6139 #endif
6140     MvAddStr(0, 0, "This is the menu test:");
6141     MvAddStr(2, 0, "  Use up and down arrow to move the select bar.");
6142     MvAddStr(3, 0, "  'n' and 'p' act like arrows.");
6143     MvAddStr(4, 0,
6144              "  'b' and 'f' scroll up/down (page), 'u' and 'd' (line).");
6145     MvAddStr(5, 0, "  Press return to exit.");
6146     refresh();
6147
6148     for (ap = animals; *ap; ap++) {
6149         if ((*ip = new_item(*ap, "")) != 0)
6150             ++ip;
6151     }
6152     *ip = (ITEM *) 0;
6153
6154     m = new_menu(items);
6155
6156     set_menu_format(m, (SIZEOF(animals) + 1) / 2, 1);
6157     scale_menu(m, &mrows, &mcols);
6158
6159     menuwin = newwin(mrows + 2, mcols + 2, MENU_Y, MENU_X);
6160     set_menu_win(m, menuwin);
6161     keypad(menuwin, TRUE);
6162     box(menuwin, 0, 0);
6163
6164     set_menu_sub(m, derwin(menuwin, mrows, mcols, 1, 1));
6165
6166     post_menu(m);
6167
6168     while ((c = menu_driver(m, menu_virtualize(wGetchar(menuwin)))) != E_UNKNOWN_COMMAND) {
6169         if (c == E_NOT_POSTED)
6170             break;
6171         if (c == E_REQUEST_DENIED)
6172             beep();
6173         continue;
6174     }
6175
6176     MvPrintw(LINES - 2, 0,
6177              "You chose: %s\n", item_name(current_item(m)));
6178     (void) addstr("Press any key to continue...");
6179     wGetchar(stdscr);
6180
6181     unpost_menu(m);
6182     delwin(menuwin);
6183
6184     free_menu(m);
6185     for (ip = items; *ip; ip++)
6186         free_item(*ip);
6187 #ifdef NCURSES_MOUSE_VERSION
6188     mousemask(0, (mmask_t *) 0);
6189 #endif
6190     return OK;
6191 }
6192
6193 #ifdef TRACE
6194 #define T_TBL(name) { #name, name }
6195 static struct {
6196     const char *name;
6197     unsigned mask;
6198 } t_tbl[] = {
6199
6200     T_TBL(TRACE_DISABLE),
6201         T_TBL(TRACE_TIMES),
6202         T_TBL(TRACE_TPUTS),
6203         T_TBL(TRACE_UPDATE),
6204         T_TBL(TRACE_MOVE),
6205         T_TBL(TRACE_CHARPUT),
6206         T_TBL(TRACE_ORDINARY),
6207         T_TBL(TRACE_CALLS),
6208         T_TBL(TRACE_VIRTPUT),
6209         T_TBL(TRACE_IEVENT),
6210         T_TBL(TRACE_BITS),
6211         T_TBL(TRACE_ICALLS),
6212         T_TBL(TRACE_CCALLS),
6213         T_TBL(TRACE_DATABASE),
6214         T_TBL(TRACE_ATTRS),
6215         T_TBL(TRACE_MAXIMUM),
6216     {
6217         (char *) 0, 0
6218     }
6219 };
6220
6221 static char *
6222 tracetrace(unsigned tlevel)
6223 {
6224     static char *buf;
6225     static size_t need = 12;
6226     int n;
6227
6228     if (buf == 0) {
6229         for (n = 0; t_tbl[n].name != 0; n++)
6230             need += strlen(t_tbl[n].name) + 2;
6231         buf = typeMalloc(char, need);
6232         if (!buf)
6233             failed("tracetrace");
6234     }
6235     _nc_SPRINTF(buf, _nc_SLIMIT(need) "0x%02x = {", tlevel);
6236     if (tlevel == 0) {
6237         _nc_STRCAT(buf, t_tbl[0].name, need);
6238         _nc_STRCAT(buf, ", ", need);
6239     } else {
6240         for (n = 1; t_tbl[n].name != 0; n++)
6241             if ((tlevel & t_tbl[n].mask) == t_tbl[n].mask) {
6242                 _nc_STRCAT(buf, t_tbl[n].name, need);
6243                 _nc_STRCAT(buf, ", ", need);
6244             }
6245     }
6246     if (buf[strlen(buf) - 2] == ',')
6247         buf[strlen(buf) - 2] = '\0';
6248     _nc_STRCAT(buf, "}", need);
6249     return buf;
6250 }
6251
6252 /* fake a dynamically reconfigurable menu using the 0th entry to deselect
6253  * the others
6254  */
6255 static int
6256 run_trace_menu(MENU * m)
6257 {
6258     ITEM **items;
6259     ITEM *i, **p;
6260
6261     for (;;) {
6262         bool changed = FALSE;
6263         switch (menu_driver(m, menu_virtualize(wGetchar(menu_win(m))))) {
6264         case E_UNKNOWN_COMMAND:
6265             return FALSE;
6266         default:
6267             items = menu_items(m);
6268             i = current_item(m);
6269             if (i == items[0]) {
6270                 if (item_value(i)) {
6271                     for (p = items + 1; *p != 0; p++)
6272                         if (item_value(*p)) {
6273                             set_item_value(*p, FALSE);
6274                             changed = TRUE;
6275                         }
6276                 }
6277             } else {
6278                 for (p = items + 1; *p != 0; p++)
6279                     if (item_value(*p)) {
6280                         set_item_value(items[0], FALSE);
6281                         changed = TRUE;
6282                         break;
6283                     }
6284             }
6285             if (!changed)
6286                 return TRUE;
6287         }
6288     }
6289 }
6290
6291 static int
6292 trace_set(bool recur GCC_UNUSED)
6293 /* interactively set the trace level */
6294 {
6295     MENU *m;
6296     ITEM *items[SIZEOF(t_tbl)];
6297     ITEM **ip = items;
6298     int mrows, mcols;
6299     unsigned newtrace;
6300     int n;
6301     WINDOW *menuwin;
6302
6303     MvAddStr(0, 0, "Interactively set trace level:");
6304     MvAddStr(2, 0, "  Press space bar to toggle a selection.");
6305     MvAddStr(3, 0, "  Use up and down arrow to move the select bar.");
6306     MvAddStr(4, 0, "  Press return to set the trace level.");
6307     MvPrintw(6, 0, "(Current trace level is %s)", tracetrace(_nc_tracing));
6308
6309     refresh();
6310
6311     for (n = 0; t_tbl[n].name != 0; n++) {
6312         if ((*ip = new_item(t_tbl[n].name, "")) != 0) {
6313             ++ip;
6314         }
6315     }
6316     *ip = (ITEM *) 0;
6317
6318     m = new_menu(items);
6319
6320     set_menu_format(m, 0, 2);
6321     scale_menu(m, &mrows, &mcols);
6322
6323     menu_opts_off(m, O_ONEVALUE);
6324     menuwin = newwin(mrows + 2, mcols + 2, MENU_Y, MENU_X);
6325     set_menu_win(m, menuwin);
6326     keypad(menuwin, TRUE);
6327     box(menuwin, 0, 0);
6328
6329     set_menu_sub(m, derwin(menuwin, mrows, mcols, 1, 1));
6330
6331     post_menu(m);
6332
6333     for (ip = menu_items(m); *ip; ip++) {
6334         unsigned mask = t_tbl[item_index(*ip)].mask;
6335         if (mask == 0)
6336             set_item_value(*ip, _nc_tracing == 0);
6337         else if ((mask & _nc_tracing) == mask)
6338             set_item_value(*ip, TRUE);
6339     }
6340
6341     while (run_trace_menu(m))
6342         continue;
6343
6344     newtrace = 0;
6345     for (ip = menu_items(m); *ip; ip++)
6346         if (item_value(*ip))
6347             newtrace |= t_tbl[item_index(*ip)].mask;
6348     trace(newtrace);
6349     Trace(("trace level interactively set to %s", tracetrace(_nc_tracing)));
6350
6351     MvPrintw(LINES - 2, 0,
6352              "Trace level is %s\n", tracetrace(_nc_tracing));
6353     (void) addstr("Press any key to continue...");
6354     wGetchar(stdscr);
6355
6356     unpost_menu(m);
6357     delwin(menuwin);
6358
6359     free_menu(m);
6360     for (ip = items; *ip; ip++)
6361         free_item(*ip);
6362
6363     return OK;
6364 }
6365 #endif /* TRACE */
6366 #endif /* USE_LIBMENU */
6367
6368 /****************************************************************************
6369  *
6370  * Forms test
6371  *
6372  ****************************************************************************/
6373 #if USE_LIBFORM
6374 static FIELD *
6375 make_label(int frow, int fcol, NCURSES_CONST char *label)
6376 {
6377     FIELD *f = new_field(1, (int) strlen(label), frow, fcol, 0, 0);
6378
6379     if (f) {
6380         set_field_buffer(f, 0, label);
6381         set_field_opts(f, (int) ((unsigned) field_opts(f) & ~O_ACTIVE));
6382     }
6383     return (f);
6384 }
6385
6386 static FIELD *
6387 make_field(int frow, int fcol, int rows, int cols, bool secure)
6388 {
6389     FIELD *f = new_field(rows, cols, frow, fcol, 0, secure ? 1 : 0);
6390
6391     if (f) {
6392         set_field_back(f, A_UNDERLINE);
6393         set_field_userptr(f, (void *) 0);
6394     }
6395     return (f);
6396 }
6397
6398 static void
6399 display_form(FORM *f)
6400 {
6401     WINDOW *w;
6402     int rows, cols;
6403
6404     scale_form(f, &rows, &cols);
6405
6406     if ((w = newwin(rows + 2, cols + 4, 0, 0)) != (WINDOW *) 0) {
6407         set_form_win(f, w);
6408         set_form_sub(f, derwin(w, rows, cols, 1, 2));
6409         box(w, 0, 0);
6410         keypad(w, TRUE);
6411         if (post_form(f) != E_OK)
6412             wrefresh(w);
6413     }
6414 }
6415
6416 static void
6417 erase_form(FORM *f)
6418 {
6419     WINDOW *w = form_win(f);
6420     WINDOW *s = form_sub(f);
6421
6422     unpost_form(f);
6423     werase(w);
6424     wrefresh(w);
6425     delwin(s);
6426     delwin(w);
6427 }
6428
6429 static int
6430 edit_secure(FIELD *me, int c)
6431 {
6432     int rows, cols, frow, fcol, nrow, nbuf;
6433
6434     if (field_info(me, &rows, &cols, &frow, &fcol, &nrow, &nbuf) == E_OK
6435         && nbuf > 0) {
6436         char *source = field_buffer(me, 1);
6437         size_t have = (source ? strlen(source) : 0) + 1;
6438         size_t need = 80 + have;
6439         char *temp = malloc(need);
6440
6441         if (temp != 0) {
6442             size_t len;
6443             _nc_STRNCPY(temp, source ? source : "", have + 1);
6444             len = (size_t) (char *) field_userptr(me);
6445             if (c <= KEY_MAX) {
6446                 if (isgraph(c) && (len + 1) < sizeof(temp)) {
6447                     temp[len++] = (char) c;
6448                     temp[len] = 0;
6449                     set_field_buffer(me, 1, temp);
6450                     c = '*';
6451                 } else {
6452                     c = 0;
6453                 }
6454             } else {
6455                 switch (c) {
6456                 case REQ_BEG_FIELD:
6457                 case REQ_CLR_EOF:
6458                 case REQ_CLR_EOL:
6459                 case REQ_DEL_LINE:
6460                 case REQ_DEL_WORD:
6461                 case REQ_DOWN_CHAR:
6462                 case REQ_END_FIELD:
6463                 case REQ_INS_CHAR:
6464                 case REQ_INS_LINE:
6465                 case REQ_LEFT_CHAR:
6466                 case REQ_NEW_LINE:
6467                 case REQ_NEXT_WORD:
6468                 case REQ_PREV_WORD:
6469                 case REQ_RIGHT_CHAR:
6470                 case REQ_UP_CHAR:
6471                     c = 0;      /* we don't want to do inline editing */
6472                     break;
6473                 case REQ_CLR_FIELD:
6474                     if (len) {
6475                         temp[0] = 0;
6476                         set_field_buffer(me, 1, temp);
6477                     }
6478                     break;
6479                 case REQ_DEL_CHAR:
6480                 case REQ_DEL_PREV:
6481                     if (len) {
6482                         temp[--len] = 0;
6483                         set_field_buffer(me, 1, temp);
6484                     }
6485                     break;
6486                 }
6487             }
6488             set_field_userptr(me, (void *) len);
6489             free(temp);
6490         }
6491     }
6492     return c;
6493 }
6494
6495 static int
6496 form_virtualize(FORM *f, WINDOW *w)
6497 {
6498     /* *INDENT-OFF* */
6499     static const struct {
6500         int code;
6501         int result;
6502     } lookup[] = {
6503         { CTRL('A'),    REQ_NEXT_CHOICE },
6504         { CTRL('B'),    REQ_PREV_WORD },
6505         { CTRL('C'),    REQ_CLR_EOL },
6506         { CTRL('D'),    REQ_DOWN_FIELD },
6507         { CTRL('E'),    REQ_END_FIELD },
6508         { CTRL('F'),    REQ_NEXT_PAGE },
6509         { CTRL('G'),    REQ_DEL_WORD },
6510         { CTRL('H'),    REQ_DEL_PREV },
6511         { CTRL('I'),    REQ_INS_CHAR },
6512         { CTRL('K'),    REQ_CLR_EOF },
6513         { CTRL('L'),    REQ_LEFT_FIELD },
6514         { CTRL('M'),    REQ_NEW_LINE },
6515         { CTRL('N'),    REQ_NEXT_FIELD },
6516         { CTRL('O'),    REQ_INS_LINE },
6517         { CTRL('P'),    REQ_PREV_FIELD },
6518         { CTRL('R'),    REQ_RIGHT_FIELD },
6519         { CTRL('S'),    REQ_BEG_FIELD },
6520         { CTRL('U'),    REQ_UP_FIELD },
6521         { CTRL('V'),    REQ_DEL_CHAR },
6522         { CTRL('W'),    REQ_NEXT_WORD },
6523         { CTRL('X'),    REQ_CLR_FIELD },
6524         { CTRL('Y'),    REQ_DEL_LINE },
6525         { CTRL('Z'),    REQ_PREV_CHOICE },
6526         { ESCAPE,       MAX_FORM_COMMAND + 1 },
6527         { KEY_BACKSPACE, REQ_DEL_PREV },
6528         { KEY_DOWN,     REQ_DOWN_CHAR },
6529         { KEY_END,      REQ_LAST_FIELD },
6530         { KEY_HOME,     REQ_FIRST_FIELD },
6531         { KEY_LEFT,     REQ_LEFT_CHAR },
6532         { KEY_LL,       REQ_LAST_FIELD },
6533         { KEY_NEXT,     REQ_NEXT_FIELD },
6534         { KEY_NPAGE,    REQ_NEXT_PAGE },
6535         { KEY_PPAGE,    REQ_PREV_PAGE },
6536         { KEY_PREVIOUS, REQ_PREV_FIELD },
6537         { KEY_RIGHT,    REQ_RIGHT_CHAR },
6538         { KEY_UP,       REQ_UP_CHAR },
6539         { QUIT,         MAX_FORM_COMMAND + 1 }
6540     };
6541     /* *INDENT-ON* */
6542
6543     static int mode = REQ_INS_MODE;
6544     int c = wGetchar(w);
6545     FIELD *me = current_field(f);
6546     bool current = TRUE;
6547
6548     if (c == CTRL(']')) {
6549         if (mode == REQ_INS_MODE) {
6550             mode = REQ_OVL_MODE;
6551         } else {
6552             mode = REQ_INS_MODE;
6553         }
6554         c = mode;
6555     } else {
6556         unsigned n;
6557         for (n = 0; n < SIZEOF(lookup); n++) {
6558             if (lookup[n].code == c) {
6559                 c = lookup[n].result;
6560                 break;
6561             }
6562         }
6563     }
6564     MvPrintw(0, COLS - 6, "(%s)", mode == REQ_INS_MODE ? "INS" : "OVL");
6565
6566     /*
6567      * Force the field that the user is typing into to be in reverse video,
6568      * while the other fields are shown underlined.
6569      */
6570     switch (c) {
6571     case REQ_BEG_FIELD:
6572     case REQ_CLR_EOF:
6573     case REQ_CLR_EOL:
6574     case REQ_CLR_FIELD:
6575     case REQ_DEL_CHAR:
6576     case REQ_DEL_LINE:
6577     case REQ_DEL_PREV:
6578     case REQ_DEL_WORD:
6579     case REQ_END_FIELD:
6580     case REQ_INS_CHAR:
6581     case REQ_INS_LINE:
6582     case REQ_LEFT_CHAR:
6583     case REQ_LEFT_FIELD:
6584     case REQ_NEXT_WORD:
6585     case REQ_RIGHT_CHAR:
6586         current = TRUE;
6587         break;
6588     default:
6589         current = (c < KEY_MAX);
6590         break;
6591     }
6592     if (current) {
6593         c = edit_secure(me, c);
6594         set_field_back(me, A_REVERSE);
6595     } else {
6596         c = edit_secure(me, c);
6597         set_field_back(me, A_UNDERLINE);
6598     }
6599     return c;
6600 }
6601
6602 static int
6603 my_form_driver(FORM *form, int c)
6604 {
6605     if (c == (MAX_FORM_COMMAND + 1)
6606         && form_driver(form, REQ_VALIDATION) == E_OK)
6607         return (TRUE);
6608     else {
6609         beep();
6610         return (FALSE);
6611     }
6612 }
6613
6614 #ifdef NCURSES_VERSION
6615 #define FIELDCHECK_CB(func) bool func(FIELD * fld, const void * data GCC_UNUSED)
6616 #define CHAR_CHECK_CB(func) bool func(int ch, const void *data GCC_UNUSED)
6617 #else
6618 #define FIELDCHECK_CB(func) int func(FIELD * fld, char * data GCC_UNUSED)
6619 #define CHAR_CHECK_CB(func) int func(int ch, char *data GCC_UNUSED)
6620 #endif
6621
6622 /*
6623  * Allow a middle initial, optionally with a '.' to end it.
6624  */
6625 static
6626 FIELDCHECK_CB(mi_field_check)
6627 {
6628     char *s = field_buffer(fld, 0);
6629     int state = 0;
6630     int n;
6631
6632     for (n = 0; s[n] != '\0'; ++n) {
6633         switch (state) {
6634         case 0:
6635             if (s[n] == '.') {
6636                 if (n != 1)
6637                     return FALSE;
6638                 state = 2;
6639             } else if (isspace(UChar(s[n]))) {
6640                 state = 2;
6641             }
6642             break;
6643         case 2:
6644             if (!isspace(UChar(s[n])))
6645                 return FALSE;
6646             break;
6647         }
6648     }
6649
6650     /* force the form to display a leading capital */
6651     if (islower(UChar(s[0]))) {
6652         s[0] = (char) toupper(UChar(s[0]));
6653         set_field_buffer(fld, 0, s);
6654     }
6655     return TRUE;
6656 }
6657
6658 static
6659 CHAR_CHECK_CB(mi_char_check)
6660 {
6661     return ((isalpha(ch) || ch == '.') ? TRUE : FALSE);
6662 }
6663
6664 /*
6665  * Passwords should be at least 6 characters.
6666  */
6667 static
6668 FIELDCHECK_CB(pw_field_check)
6669 {
6670     char *s = field_buffer(fld, 0);
6671     int n;
6672
6673     for (n = 0; s[n] != '\0'; ++n) {
6674         if (isspace(UChar(s[n]))) {
6675             if (n < 6)
6676                 return FALSE;
6677         }
6678     }
6679     return TRUE;
6680 }
6681
6682 static
6683 CHAR_CHECK_CB(pw_char_check)
6684 {
6685     return (isgraph(ch) ? TRUE : FALSE);
6686 }
6687
6688 static int
6689 form_test(bool recur GCC_UNUSED)
6690 {
6691     FORM *form;
6692     FIELD *f[12], *secure;
6693     FIELDTYPE *fty_middle = new_fieldtype(mi_field_check, mi_char_check);
6694     FIELDTYPE *fty_passwd = new_fieldtype(pw_field_check, pw_char_check);
6695     int c;
6696     unsigned n = 0;
6697
6698 #ifdef NCURSES_MOUSE_VERSION
6699     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
6700 #endif
6701
6702     move(18, 0);
6703     addstr("Defined edit/traversal keys:   ^Q/ESC- exit form\n");
6704     addstr("^N   -- go to next field       ^P  -- go to previous field\n");
6705     addstr("Home -- go to first field      End -- go to last field\n");
6706     addstr("^L   -- go to field to left    ^R  -- go to field to right\n");
6707     addstr("^U   -- move upward to field   ^D  -- move downward to field\n");
6708     addstr("^W   -- go to next word        ^B  -- go to previous word\n");
6709     addstr("^S   -- go to start of field   ^E  -- go to end of field\n");
6710     addstr("^H   -- delete previous char   ^Y  -- delete line\n");
6711     addstr("^G   -- delete current word    ^C  -- clear to end of line\n");
6712     addstr("^K   -- clear to end of field  ^X  -- clear field\n");
6713     addstr("Arrow keys move within a field as you would expect. ^] toggles overlay mode.");
6714
6715     MvAddStr(4, 57, "Forms Entry Test");
6716
6717     refresh();
6718
6719     /* describe the form */
6720     memset(f, 0, sizeof(f));
6721     f[n++] = make_label(0, 15, "Sample Form");
6722
6723     f[n++] = make_label(2, 0, "Last Name");
6724     f[n++] = make_field(3, 0, 1, 18, FALSE);
6725     set_field_type(f[n - 1], TYPE_ALPHA, 1);
6726
6727     f[n++] = make_label(2, 20, "First Name");
6728     f[n++] = make_field(3, 20, 1, 12, FALSE);
6729     set_field_type(f[n - 1], TYPE_ALPHA, 1);
6730
6731     f[n++] = make_label(2, 34, "Middle Name");
6732     f[n++] = make_field(3, 34, 1, 12, FALSE);
6733     set_field_type(f[n - 1], fty_middle);
6734
6735     f[n++] = make_label(5, 0, "Comments");
6736     f[n++] = make_field(6, 0, 4, 46, FALSE);
6737
6738     f[n++] = make_label(5, 20, "Password:");
6739     secure =
6740         f[n++] = make_field(5, 30, 1, 9, TRUE);
6741     set_field_type(f[n - 1], fty_passwd);
6742     f[n] = (FIELD *) 0;
6743
6744     if ((form = new_form(f)) != 0) {
6745         WINDOW *w;
6746         int finished = 0;
6747
6748         display_form(form);
6749
6750         w = form_win(form);
6751         raw();
6752         nonl();                 /* lets us read ^M's */
6753         while (!finished) {
6754             switch (form_driver(form, c = form_virtualize(form, w))) {
6755             case E_OK:
6756                 MvAddStr(5, 57, field_buffer(secure, 1));
6757                 clrtoeol();
6758                 refresh();
6759                 break;
6760             case E_UNKNOWN_COMMAND:
6761                 finished = my_form_driver(form, c);
6762                 break;
6763             default:
6764                 beep();
6765                 break;
6766             }
6767         }
6768
6769         erase_form(form);
6770
6771         free_form(form);
6772     }
6773     for (c = 0; f[c] != 0; c++)
6774         free_field(f[c]);
6775     free_fieldtype(fty_middle);
6776     free_fieldtype(fty_passwd);
6777     noraw();
6778     nl();
6779
6780 #ifdef NCURSES_MOUSE_VERSION
6781     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
6782 #endif
6783     return OK;
6784 }
6785 #endif /* USE_LIBFORM */
6786
6787 /****************************************************************************
6788  *
6789  * Overlap test
6790  *
6791  ****************************************************************************/
6792
6793 #if HAVE_COPYWIN                /* ...and overlay, overwrite */
6794
6795 static const int overlap_HEAD = 1;
6796 static const int overlap_FOOT = 6;
6797
6798 static WINDOW *
6799 make_overlap(int n)
6800 {
6801     WINDOW *result;
6802     int y, x;
6803
6804     getmaxyx(stdscr, y, x);
6805     if (y < 23 || x < 80) {
6806         Cannot("The screen is too small for this test");
6807         result = 0;
6808     } else {
6809         int ymax = y - (overlap_HEAD + overlap_FOOT);
6810         int high = ymax / 5;    /* equal-sized parts for cross */
6811         int xmax = x - 2;       /* margin */
6812         int wide = (xmax / 5) & ~1;
6813         int lmar, tmar;
6814
6815         if (high > 8)
6816             high = 8;
6817
6818         if (wide > 8)
6819             wide = 8;
6820
6821         tmar = (ymax - (5 * high)) / 2 + overlap_HEAD;
6822         lmar = (xmax - (5 * wide)) / 2;
6823
6824         if (n == 0) {
6825             result = newwin(3 * high, 3 * wide, tmar, lmar);
6826         } else {
6827             result = newwin(3 * high, 3 * wide, tmar + 2 * high, lmar + 2 * wide);
6828         }
6829     }
6830     return result;
6831 }
6832
6833 static void
6834 clear_overlap(void)
6835 {
6836     int row;
6837
6838     for (row = overlap_HEAD; row < LINES - overlap_FOOT; ++row) {
6839         move(row, 0);
6840         clrtoeol();
6841     }
6842 }
6843
6844 static int
6845 move_overlap(int shift, WINDOW *win1)
6846 {
6847     int ymax = getmaxy(stdscr) - (overlap_HEAD + overlap_FOOT);
6848     int high = ymax / 5;        /* equal-sized parts for cross */
6849     int tmar;
6850     int xmax1 = getmaxx(win1) + 1;
6851     int lmar1 = (COLS - (5 * (xmax1) / 3)) / 2;
6852     int rc = ERR;
6853
6854     if (high > 8)
6855         high = 8;
6856     tmar = (ymax - (5 * high)) / 2 + overlap_HEAD;
6857
6858     rc = mvwin(win1, tmar, lmar1 + shift);
6859     return rc;
6860 }
6861
6862 static void
6863 fillwin(WINDOW *win, char ch)
6864 {
6865     int y, x;
6866     int y1, x1;
6867
6868     getmaxyx(win, y1, x1);
6869     for (y = 0; y < y1; y++) {
6870         wmove(win, y, 0);
6871         for (x = 0; x < x1; x++)
6872             waddch(win, UChar(ch));
6873     }
6874 }
6875
6876 #define InCross(x,y, x1,y1) \
6877             (((x > (x1 - 1) / 3) && (x <= (2 * (x1 - 1)) / 3)) \
6878                 || (((y > (y1 - 1) / 3) && (y <= (2 * (y1 - 1)) / 3))))
6879
6880 static void
6881 crosswin(WINDOW *win, char ch)
6882 {
6883     int y, x;
6884     int y1, x1;
6885     int xw = 1;
6886
6887     getmaxyx(win, y1, x1);
6888     for (y = 0; y < y1; y++) {
6889         for (x = 0; x < x1; x += xw) {
6890             if (InCross(x, y, x1, y1)) {
6891                 wmove(win, y, x);
6892                 waddch(win, UChar(ch));
6893             }
6894         }
6895     }
6896 }
6897
6898 /*
6899  * Match "crosswin()", but using line-drawing characters.  This could be done
6900  * a little simpler using box(), but the reason for this example is to test
6901  * hline/vline and addch with line-drawing vs the copy/overlay functions.
6902  */
6903 static void
6904 crossbox(WINDOW *win)
6905 {
6906     int y1, x1;
6907     int ymax, xmax;
6908
6909     getmaxyx(win, y1, x1);
6910
6911     ymax = (y1 + 1);
6912     xmax = (x1 + 1);
6913
6914     mvwhline(win, 0, (xmax / 3), ACS_HLINE, (xmax / 3));
6915     mvwhline(win, ymax / 3, 0, ACS_HLINE, xmax);
6916     mvwhline(win, ((2 * ymax) / 3) - 1, 0, ACS_HLINE, xmax);
6917     mvwhline(win, y1 - 1, (xmax / 3), ACS_HLINE, (xmax / 3));
6918
6919     mvwvline(win, (ymax / 3), 0, ACS_VLINE, (ymax / 3));
6920     mvwvline(win, 0, xmax / 3, ACS_VLINE, ymax);
6921     mvwvline(win, 0, ((2 * xmax) / 3) - 1, ACS_VLINE, ymax);
6922     mvwvline(win, (ymax / 3), x1 - 1, ACS_VLINE, (ymax / 3));
6923
6924     mvwaddch(win, 0, (xmax / 3), ACS_ULCORNER);
6925     mvwaddch(win, 0, ((2 * xmax) / 3) - 1, ACS_URCORNER);
6926     mvwaddch(win, y1 - 1, (xmax / 3), ACS_LLCORNER);
6927     mvwaddch(win, y1 - 1, ((2 * xmax) / 3) - 1, ACS_LRCORNER);
6928
6929     mvwaddch(win, (ymax / 3), 0, ACS_ULCORNER);
6930     mvwaddch(win, ((2 * ymax) / 3) - 1, 0, ACS_LLCORNER);
6931     mvwaddch(win, (ymax / 3), x1 - 1, ACS_URCORNER);
6932     mvwaddch(win, ((2 * ymax) / 3) - 1, x1 - 1, ACS_LRCORNER);
6933
6934     mvwaddch(win, (ymax / 3), (xmax / 3), ACS_PLUS);
6935     mvwaddch(win, (ymax / 3), ((2 * xmax) / 3) - 1, ACS_PLUS);
6936     mvwaddch(win, ((2 * ymax) / 3) - 1, ((2 * xmax) / 3) - 1, ACS_PLUS);
6937     mvwaddch(win, ((2 * ymax) / 3) - 1, (xmax / 3), ACS_PLUS);
6938 }
6939
6940 typedef enum {
6941     otBASE_refresh = 0
6942     ,otBASE_fill
6943     ,otBASE_draw
6944     ,otBASE_clear
6945     ,otBASE_copy
6946 } otBASE;
6947
6948 #define OVERLAP_FLAVORS 6
6949
6950 typedef enum {
6951     otFILL_normal = 0
6952     ,otFILL_bold
6953     ,otFILL_color
6954     ,otFILL_bright
6955 } otFILL;
6956
6957 #define LimitFILL() UseColors ? 4 : 2
6958
6959 typedef enum {
6960     otDRAW_text_cross = 0
6961     ,otDRAW_line_box
6962     ,otDRAW_line_cross
6963     ,otDRAW_set_bg
6964     ,otDRAW_reset_bg
6965 } otDRAW;
6966
6967 #define LimitDRAW() UseColors ? 5 : 3
6968
6969 typedef enum {
6970     otCOPY_overwrite = 0
6971     ,otCOPY_merge
6972     ,otCOPY_force
6973     ,otCOPY_overlay
6974 } otCOPY;
6975
6976 #define LimitCOPY() 4
6977
6978 static void
6979 overlap_helpitem(int state, int item, char *message)
6980 {
6981     int row = (item / 2);
6982     int col = ((item % 2) ? COLS / 2 : 0);
6983
6984     move(LINES - 6 + row, col);
6985     printw("%c%c = %s", state == row ? '>' : ' ', 'a' + item, message);
6986     clrtoeol();
6987 }
6988
6989 static void
6990 overlap_test_1_attr(WINDOW *win, int flavor, int col)
6991 {
6992     NCURSES_PAIRS_T cpair = (NCURSES_PAIRS_T) (1 + (flavor * 2) + col);
6993
6994     switch ((otFILL) flavor) {
6995     case otFILL_normal:
6996         (void) wattrset(win, A_NORMAL);
6997         break;
6998     case otFILL_bold:
6999         (void) wattrset(win, A_BOLD);
7000         break;
7001     case otFILL_color:
7002         init_pair(cpair, COLOR_BLUE, COLOR_WHITE);
7003         (void) wattrset(win, AttrArg(COLOR_PAIR(cpair), A_NORMAL));
7004         break;
7005     case otFILL_bright:
7006         init_pair(cpair, COLOR_WHITE, COLOR_BLUE);
7007         (void) wattrset(win, AttrArg(COLOR_PAIR(cpair), A_BOLD));
7008         break;
7009     }
7010 }
7011
7012 static void
7013 overlap_test_2_attr(WINDOW *win, int flavor, int col)
7014 {
7015     NCURSES_PAIRS_T cpair = (NCURSES_PAIRS_T) (9 + (flavor * 2) + col);
7016
7017     switch ((otDRAW) flavor) {
7018     case otDRAW_text_cross:
7019         /* no effect */
7020         break;
7021     case otDRAW_line_box:
7022         /* no effect */
7023         break;
7024     case otDRAW_line_cross:
7025         /* no effect */
7026         break;
7027     case otDRAW_set_bg:
7028         init_pair(cpair, COLOR_RED, COLOR_GREEN);
7029         wbkgdset(win, colored_chtype(' ', A_BLINK, cpair));
7030         break;
7031     case otDRAW_reset_bg:
7032         wbkgdset(win, ' ' | A_NORMAL);
7033         break;
7034     }
7035 }
7036
7037 static int
7038 overlap_help(int state, int flavors[OVERLAP_FLAVORS])
7039 {
7040     int item;
7041     int limit[OVERLAP_FLAVORS];
7042     char msg[80];
7043
7044     if (state < 0)
7045         state += OVERLAP_FLAVORS;
7046     state = state % OVERLAP_FLAVORS;
7047     assert(state >= 0 && state < OVERLAP_FLAVORS);
7048
7049     for (item = 0; item < (2 * OVERLAP_FLAVORS); ++item) {
7050         int row = item / 2;
7051         int col = item % 2;
7052         const char *ths = col ? "B" : "A";
7053         const char *tht = col ? "A" : "B";
7054
7055         switch ((otBASE) row) {
7056         case otBASE_refresh:
7057             limit[row] = 1;
7058             flavors[row] = 0;
7059             _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7060                         "refresh %s, then %s, then doupdate.", ths, tht);
7061             break;
7062         case otBASE_fill:
7063             limit[row] = LimitFILL();
7064             flavors[row] %= limit[row];
7065             overlap_test_1_attr(stdscr, flavors[row], col);
7066             _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7067                         "fill window %s with letter %s.", ths, ths);
7068             break;
7069         case otBASE_draw:
7070             limit[row] = LimitDRAW();
7071             flavors[row] %= limit[row];
7072             switch ((otDRAW) flavors[row]) {
7073             case otDRAW_text_cross:
7074                 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7075                             "cross text-pattern in window %s.", ths);
7076                 break;
7077             case otDRAW_line_box:
7078                 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7079                             "draw line-box in window %s.", ths);
7080                 break;
7081             case otDRAW_line_cross:
7082                 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7083                             "draw line-cross in window %s.", ths);
7084                 break;
7085             case otDRAW_set_bg:
7086                 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7087                             "set background of window %s.", ths);
7088                 break;
7089             case otDRAW_reset_bg:
7090                 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7091                             "reset background of window %s.", ths);
7092                 break;
7093             }
7094             break;
7095         case otBASE_clear:
7096             limit[row] = 1;
7097             flavors[row] = 0;
7098             _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7099                         "clear window %s.", ths);
7100             break;
7101         case otBASE_copy:
7102             limit[row] = LimitCOPY();
7103             flavors[row] %= limit[row];
7104             switch ((otCOPY) flavors[row]) {
7105             case otCOPY_overwrite:
7106                 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7107                             "overwrite %s onto %s.", ths, tht);
7108                 break;
7109             case otCOPY_merge:
7110                 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7111                             "copywin(FALSE) %s onto %s.", ths, tht);
7112                 break;
7113             case otCOPY_force:
7114                 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7115                             "copywin(TRUE) %s onto %s.", ths, tht);
7116                 break;
7117             case otCOPY_overlay:
7118                 _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7119                             "overlay %s onto %s.", ths, tht);
7120                 break;
7121             }
7122             break;
7123         }
7124         overlap_helpitem(state, item, msg);
7125         (void) wattrset(stdscr, A_NORMAL);
7126         wbkgdset(stdscr, ' ' | A_NORMAL);
7127     }
7128     move(LINES - 1, 0);
7129     printw("^Q/ESC = terminate test. </> shift. Up/down/space select (row %d",
7130            state + 1);
7131     if (limit[state] > 1)
7132         printw(" test %d:%d", 1 + flavors[state], limit[state]);
7133     printw(").");
7134     clrtoeol();
7135
7136     return state;
7137 }
7138
7139 static void
7140 overlap_test_0(WINDOW *a, WINDOW *b)
7141 {
7142     touchwin(a);
7143     touchwin(b);
7144     wnoutrefresh(a);
7145     wnoutrefresh(b);
7146     doupdate();
7147 }
7148
7149 static void
7150 overlap_test_1(int flavor, int col, WINDOW *a, char fill)
7151 {
7152     overlap_test_1_attr(a, flavor, col);
7153     fillwin(a, fill);
7154     (void) wattrset(a, A_NORMAL);
7155 }
7156
7157 static void
7158 overlap_test_2(int flavor, int col, WINDOW *a, char fill)
7159 {
7160     overlap_test_2_attr(a, flavor, col);
7161     switch ((otDRAW) flavor) {
7162     case otDRAW_text_cross:
7163         crosswin(a, fill);
7164         break;
7165     case otDRAW_line_box:
7166         box(a, 0, 0);
7167         break;
7168     case otDRAW_line_cross:
7169         crossbox(a);
7170         break;
7171     case otDRAW_set_bg:
7172         /* done in overlap_test_2_attr */
7173         break;
7174     case otDRAW_reset_bg:
7175         /* done in overlap_test_2_attr */
7176         break;
7177     }
7178 }
7179
7180 static void
7181 overlap_test_3(WINDOW *a)
7182 {
7183     wclear(a);
7184     wmove(a, 0, 0);
7185 }
7186
7187 static void
7188 overlap_test_4(int flavor, WINDOW *a, WINDOW *b)
7189 {
7190     switch ((otCOPY) flavor) {
7191     case otCOPY_overwrite:
7192         overwrite(a, b);
7193         break;
7194     case otCOPY_merge:
7195         copywin(a, b, 0, 0, 0, 0, getmaxy(b), getmaxx(b), FALSE);
7196         break;
7197     case otCOPY_force:
7198         copywin(a, b, 0, 0, 0, 0, getmaxy(b), getmaxx(b), TRUE);
7199         break;
7200     case otCOPY_overlay:
7201         overlay(a, b);
7202         break;
7203     }
7204 }
7205
7206 /* test effects of overlapping windows */
7207 static int
7208 overlap_test(bool recur GCC_UNUSED)
7209 {
7210     WINDOW *win1, *win2;
7211     int ch;
7212     int shift = 0, last_refresh = -1;
7213     int state, flavor[OVERLAP_FLAVORS];
7214
7215     if ((win1 = make_overlap(0)) == 0) {
7216         return ERR;
7217     } else if ((win2 = make_overlap(1)) == 0) {
7218         delwin(win1);
7219         return ERR;
7220     }
7221
7222     curs_set(0);
7223     raw();
7224     refresh();
7225     move(0, 0);
7226     printw("Test wnoutrefresh() for two overlapping windows:");
7227
7228     memset(flavor, 0, sizeof(flavor));
7229     state = overlap_help(0, flavor);
7230
7231     while (!isQuit(ch = Getchar(), TRUE)) {
7232         switch (ch) {
7233         case 'a':               /* refresh window A first, then B */
7234             overlap_test_0(win1, win2);
7235             break;
7236
7237         case 'b':               /* refresh window B first, then A */
7238             overlap_test_0(win2, win1);
7239             break;
7240
7241         case 'c':               /* fill window A so it's visible */
7242             overlap_test_1(flavor[otBASE_fill], 0, win1, 'A');
7243             break;
7244
7245         case 'd':               /* fill window B so it's visible */
7246             overlap_test_1(flavor[otBASE_fill], 1, win2, 'B');
7247             break;
7248
7249         case 'e':               /* cross test pattern in window A */
7250             overlap_test_2(flavor[otBASE_draw], 0, win1, 'A');
7251             break;
7252
7253         case 'f':               /* cross test pattern in window A */
7254             overlap_test_2(flavor[otBASE_draw], 1, win2, 'B');
7255             break;
7256
7257         case 'g':               /* clear window A */
7258             overlap_test_3(win1);
7259             break;
7260
7261         case 'h':               /* clear window B */
7262             overlap_test_3(win2);
7263             break;
7264
7265         case 'i':               /* overwrite A onto B */
7266             overlap_test_4(flavor[otBASE_copy], win1, win2);
7267             break;
7268
7269         case 'j':               /* overwrite B onto A */
7270             overlap_test_4(flavor[otBASE_copy], win2, win1);
7271             break;
7272
7273         case CTRL('n'):
7274         case KEY_DOWN:
7275             state = overlap_help(state + 1, flavor);
7276             break;
7277
7278         case CTRL('p'):
7279         case KEY_UP:
7280             state = overlap_help(state - 1, flavor);
7281             break;
7282
7283         case ' ':
7284             flavor[state] += 1;
7285             state = overlap_help(state, flavor);
7286             break;
7287
7288         case HELP_KEY_1:
7289             state = overlap_help(state, flavor);
7290             break;
7291
7292         case '<':
7293             /* FALLTHRU */
7294         case '>':
7295             /* see below */
7296             break;
7297
7298         default:
7299             beep();
7300             break;
7301         }
7302
7303         switch (ch) {
7304         case 'a':
7305             /* FALLTHRU */
7306         case 'b':
7307             last_refresh = ch;
7308             break;
7309         case '<':
7310             shift -= 2;
7311             /* FALLTHRU */
7312         case '>':
7313             shift += 1;
7314             if (move_overlap(shift, win1) != OK) {
7315                 flash();
7316                 shift += (ch == '>') ? -1 : 1;
7317             } else if (last_refresh > 0) {
7318                 clear_overlap();
7319                 wnoutrefresh(stdscr);
7320                 if (last_refresh == 'a')
7321                     overlap_test_0(win1, win2);
7322                 else
7323                     overlap_test_0(win2, win1);
7324             }
7325             break;
7326         default:
7327             last_refresh = -1;
7328             break;
7329         }
7330     }
7331
7332     delwin(win2);
7333     delwin(win1);
7334     erase();
7335     exit_curses();
7336     return OK;
7337 }
7338
7339 #if USE_WIDEC_SUPPORT
7340 static void
7341 x_fillwin(WINDOW *win, wchar_t ch)
7342 {
7343     int y, x;
7344     int y1, x1;
7345
7346     getmaxyx(win, y1, x1);
7347     x1 /= 2;
7348     for (y = 0; y < y1; y++) {
7349         wmove(win, y, 0);
7350         for (x = 0; x < x1; x++)
7351             waddnwstr(win, &ch, 1);
7352     }
7353 }
7354
7355 static void
7356 x_crosswin(WINDOW *win, wchar_t ch)
7357 {
7358     int y, x;
7359     int y1, x1;
7360     int xw = 2;
7361
7362     getmaxyx(win, y1, x1);
7363     for (y = 0; y < y1; y++) {
7364         for (x = 0; x < x1; x += xw) {
7365             if (InCross(x, y, x1, y1)) {
7366                 wmove(win, y, x);
7367                 waddnwstr(win, &ch, 1);
7368             }
7369         }
7370     }
7371 }
7372
7373 static void
7374 x_overlap_test_1(int flavor, int col, WINDOW *a, wchar_t fill)
7375 {
7376     overlap_test_1_attr(a, flavor, col);
7377     x_fillwin(a, fill);
7378     (void) wattrset(a, A_NORMAL);
7379 }
7380
7381 static void
7382 x_overlap_test_2(int flavor, int col, WINDOW *a, wchar_t fill)
7383 {
7384     overlap_test_2_attr(a, flavor, col);
7385     switch ((otDRAW) flavor) {
7386     case otDRAW_text_cross:
7387         x_crosswin(a, fill);
7388         break;
7389     case otDRAW_line_box:
7390         box(a, 0, 0);
7391         break;
7392     case otDRAW_line_cross:
7393         crossbox(a);
7394         break;
7395     case otDRAW_set_bg:
7396         /* done in overlap_test_2_attr */
7397         break;
7398     case otDRAW_reset_bg:
7399         /* done in overlap_test_2_attr */
7400         break;
7401     }
7402 }
7403
7404 /* test effects of overlapping windows */
7405 static int
7406 x_overlap_test(bool recur GCC_UNUSED)
7407 {
7408     const wchar_t WIDE_A = 0xff21;
7409     const wchar_t WIDE_B = 0xff22;
7410     WINDOW *win1, *win2;
7411     int ch;
7412     int shift = 0, last_refresh = -1;
7413     int state, flavor[OVERLAP_FLAVORS];
7414
7415     if ((win1 = make_overlap(0)) == 0) {
7416         return ERR;
7417     } else if ((win2 = make_overlap(1)) == 0) {
7418         delwin(win1);
7419         return ERR;
7420     }
7421
7422     curs_set(0);
7423     raw();
7424     refresh();
7425     move(0, 0);
7426     printw("Test wnoutrefresh() for overlapping windows with double-cell characters:");
7427
7428     memset(flavor, 0, sizeof(flavor));
7429     state = overlap_help(0, flavor);
7430
7431     while (!isQuit(ch = Getchar(), TRUE)) {
7432         switch (ch) {
7433         case 'a':               /* refresh window A first, then B */
7434             overlap_test_0(win1, win2);
7435             break;
7436
7437         case 'b':               /* refresh window B first, then A */
7438             overlap_test_0(win2, win1);
7439             break;
7440
7441         case 'c':               /* fill window A so it's visible */
7442             x_overlap_test_1(flavor[otBASE_fill], 0, win1, WIDE_A);
7443             break;
7444
7445         case 'd':               /* fill window B so it's visible */
7446             x_overlap_test_1(flavor[otBASE_fill], 1, win2, WIDE_B);
7447             break;
7448
7449         case 'e':               /* cross test pattern in window A */
7450             x_overlap_test_2(flavor[otBASE_draw], 0, win1, WIDE_A);
7451             break;
7452
7453         case 'f':               /* cross test pattern in window A */
7454             x_overlap_test_2(flavor[otBASE_draw], 1, win2, WIDE_B);
7455             break;
7456
7457         case 'g':               /* clear window A */
7458             overlap_test_3(win1);
7459             break;
7460
7461         case 'h':               /* clear window B */
7462             overlap_test_3(win2);
7463             break;
7464
7465         case 'i':               /* overwrite A onto B */
7466             overlap_test_4(flavor[otBASE_copy], win1, win2);
7467             break;
7468
7469         case 'j':               /* overwrite B onto A */
7470             overlap_test_4(flavor[otBASE_copy], win2, win1);
7471             break;
7472
7473         case CTRL('n'):
7474         case KEY_DOWN:
7475             state = overlap_help(state + 1, flavor);
7476             break;
7477
7478         case CTRL('p'):
7479         case KEY_UP:
7480             state = overlap_help(state - 1, flavor);
7481             break;
7482
7483         case ' ':
7484             flavor[state] += 1;
7485             state = overlap_help(state, flavor);
7486             break;
7487
7488         case HELP_KEY_1:
7489             state = overlap_help(state, flavor);
7490             break;
7491
7492         case '<':
7493             /* FALLTHRU */
7494         case '>':
7495             /* see below */
7496             break;
7497
7498         default:
7499             beep();
7500             break;
7501         }
7502
7503         switch (ch) {
7504         case 'a':
7505             /* FALLTHRU */
7506         case 'b':
7507             last_refresh = ch;
7508             break;
7509         case '<':
7510             shift -= 2;
7511             /* FALLTHRU */
7512         case '>':
7513             shift += 1;
7514             if (move_overlap(shift, win1) != OK) {
7515                 flash();
7516                 shift += (ch == '>') ? -1 : 1;
7517             } else if (last_refresh > 0) {
7518                 clear_overlap();
7519                 wnoutrefresh(stdscr);
7520                 if (last_refresh == 'a')
7521                     overlap_test_0(win1, win2);
7522                 else
7523                     overlap_test_0(win2, win1);
7524             }
7525             break;
7526         default:
7527             last_refresh = -1;
7528             break;
7529         }
7530     }
7531
7532     delwin(win2);
7533     delwin(win1);
7534     erase();
7535     exit_curses();
7536     return OK;
7537 }
7538 #endif /* USE_WIDEC_SUPPORT */
7539
7540 #endif /* HAVE_COPYWIN */
7541
7542 static void
7543 show_setting_name(const char *name)
7544 {
7545     printw("%-25s ", name);
7546 }
7547
7548 static void
7549 show_string_setting(const char *name, const char *value)
7550 {
7551     show_setting_name(name);
7552     if (value) {
7553         printw("\"%s\"", value);
7554     } else {
7555         attron(A_REVERSE);
7556         addstr("<NULL>");
7557         attroff(A_REVERSE);
7558     }
7559     AddCh('\n');
7560 }
7561
7562 static void
7563 show_number_setting(const char *name, int value)
7564 {
7565     show_setting_name(name);
7566     if (value >= 0) {
7567         printw("%d", value);
7568     } else {
7569         attron(A_REVERSE);
7570         printw("%d", value);
7571         attroff(A_REVERSE);
7572     }
7573     AddCh('\n');
7574 }
7575
7576 static void
7577 show_boolean_setting(const char *name, int value)
7578 {
7579     show_setting_name(name);
7580     if (value >= 0) {
7581         printw("%s", value ? "TRUE" : "FALSE");
7582     } else {
7583         attron(A_REVERSE);
7584         printw("%d", value);
7585         attroff(A_REVERSE);
7586     }
7587     AddCh('\n');
7588 }
7589
7590 static int
7591 settings_test(bool recur GCC_UNUSED)
7592 {
7593 #if USE_WIDEC_SUPPORT
7594     wchar_t ch;
7595 #endif
7596
7597     move(0, 0);
7598     show_string_setting("termname", termname());
7599     show_string_setting("longname", longname());
7600     show_number_setting("baudrate", baudrate());
7601     if (erasechar() > 0) {
7602         show_string_setting("unctrl(erasechar)", unctrl((chtype) erasechar()));
7603         show_string_setting("keyname(erasechar)", keyname(erasechar()));
7604     }
7605     if (killchar() > 0) {
7606         show_string_setting("unctrl(killchar)", unctrl((chtype) killchar()));
7607         show_string_setting("keyname(killchar)", keyname(killchar()));
7608     }
7609 #if USE_WIDEC_SUPPORT
7610     if (erasewchar(&ch) == OK) {
7611         show_string_setting("key_name(erasewchar)", key_name(ch));
7612     }
7613     if (killwchar(&ch) == OK) {
7614         show_string_setting("key_name(killwchar)", key_name(ch));
7615     }
7616 #endif
7617     show_boolean_setting("has_ic", has_ic());
7618     show_boolean_setting("has_il", has_il());
7619     show_boolean_setting("has_colors", has_colors());
7620 #if HAVE_COLOR_CONTENT
7621     show_boolean_setting("can_change_color", can_change_color());
7622 #endif
7623     Pause();
7624     erase();
7625     exit_curses();
7626     return OK;
7627 }
7628
7629 /****************************************************************************
7630  *
7631  * Main sequence
7632  *
7633  ****************************************************************************/
7634
7635 static void
7636 usage(void)
7637 {
7638     static const char *const tbl[] =
7639     {
7640         "Usage: ncurses [options]"
7641         ,""
7642         ,"Options:"
7643 #ifdef NCURSES_VERSION
7644         ,"  -a f,b   set default-colors (assumed white-on-black)"
7645         ,"  -d       use default-colors if terminal supports them"
7646 #endif
7647 #if HAVE_USE_ENV
7648         ,"  -E       call use_env(FALSE) to ignore $LINES and $COLUMNS"
7649 #endif
7650 #if USE_SOFTKEYS
7651         ,"  -e fmt   specify format for soft-keys test (e)"
7652 #endif
7653 #if HAVE_RIPOFFLINE
7654         ,"  -f       rip-off footer line (can repeat)"
7655         ,"  -h       rip-off header line (can repeat)"
7656 #endif
7657         ,"  -m       do not use colors"
7658 #if HAVE_COLOR_CONTENT
7659         ,"  -p file  rgb values to use in 'd' rather than ncurses's builtin"
7660 #endif
7661 #if USE_LIBPANEL
7662         ,"  -s msec  specify nominal time for panel-demo (default: 1, to hold)"
7663 #endif
7664 #if defined(NCURSES_VERSION_PATCH) && (NCURSES_VERSION_PATCH >= 20120714) && !defined(_WIN32)
7665         ,"  -T       call use_tioctl(TRUE) to allow SIGWINCH to override environment"
7666 #endif
7667 #ifdef TRACE
7668         ,"  -t mask  specify default trace-level (may toggle with ^T)"
7669 #endif
7670 #if HAVE_COLOR_CONTENT
7671         ,"  -x       use xterm-compatible control for reading color palette"
7672 #endif
7673     };
7674     size_t n;
7675     for (n = 0; n < SIZEOF(tbl); n++)
7676         fprintf(stderr, "%s\n", tbl[n]);
7677     ExitProgram(EXIT_FAILURE);
7678 }
7679
7680 static void
7681 set_terminal_modes(void)
7682 {
7683     noraw();
7684     cbreak();
7685     noecho();
7686     scrollok(stdscr, TRUE);
7687     idlok(stdscr, TRUE);
7688     keypad(stdscr, TRUE);
7689 }
7690
7691 #ifdef SIGUSR1
7692 static void
7693 announce_sig(int sig)
7694 {
7695     (void) fprintf(stderr, "Handled signal %d\r\n", sig);
7696 }
7697 #endif
7698
7699 #if HAVE_RIPOFFLINE
7700 static int
7701 rip_footer(WINDOW *win, int cols)
7702 {
7703     wbkgd(win, A_REVERSE);
7704     werase(win);
7705     wmove(win, 0, 0);
7706     wprintw(win, "footer: window %p, %d columns", (void *) win, cols);
7707     wnoutrefresh(win);
7708     return OK;
7709 }
7710
7711 static int
7712 rip_header(WINDOW *win, int cols)
7713 {
7714     wbkgd(win, A_REVERSE);
7715     werase(win);
7716     wmove(win, 0, 0);
7717     wprintw(win, "header: window %p, %d columns", (void *) win, cols);
7718     wnoutrefresh(win);
7719     return OK;
7720 }
7721 #endif /* HAVE_RIPOFFLINE */
7722
7723 static void
7724 main_menu(bool top)
7725 {
7726 #if USE_WIDEC_SUPPORT
7727     typedef struct {
7728         bool recur;
7729         int (*narrow_func) (bool);
7730         int (*wide_func) (bool);
7731         int code;
7732         const char *help;
7733     } MyCmds;
7734 #define BOTH(a)   a, x_ ## a
7735 #define ONLY(a)   a, NULL
7736 #define CMDS(recur, funcs,code,help) { recur, funcs, code, help }
7737 #else
7738     typedef struct {
7739         bool recur;
7740         int (*narrow_func) (bool);
7741         int code;
7742         const char *help;
7743     } MyCmds;
7744 #define BOTH(a)   a
7745 #define ONLY(a)   a
7746 #define CMDS(recur, funcs,code,help) { recur, funcs, code, help }
7747 #endif
7748     /* *INDENT-OFF* */
7749     static MyCmds cmds[] =
7750     {
7751         CMDS(TRUE, BOTH(getch_test),    'a', "keyboard and mouse input test"),
7752         CMDS(TRUE, BOTH(attr_test),     'b', "character attribute test"),
7753         CMDS(TRUE, BOTH(color_test),    'c', "color test pattern"),
7754 #if HAVE_COLOR_CONTENT
7755         CMDS(FALSE, ONLY(color_edit),   'd', "edit RGB color values"),
7756 #endif
7757 #if USE_SOFTKEYS
7758         CMDS(TRUE, BOTH(slk_test),      'e', "exercise soft keys"),
7759 #endif
7760         CMDS(TRUE, BOTH(acs_test),      'f', "display ACS characters"),
7761         CMDS(TRUE, ONLY(scroll_test),   'g', "display windows and scrolling"),
7762         CMDS(TRUE, ONLY(flushinp_test), 'i', "test flushinp()"),
7763         CMDS(TRUE, ONLY(sgr_attr_test), 'k', "display character attributes"),
7764 #if USE_LIBMENU
7765         CMDS(TRUE, ONLY(menu_test),     'm', "exercise menu library"),
7766 #endif
7767 #if USE_LIBPANEL
7768         CMDS(TRUE, BOTH(panel_test),    'o', "exercise panel library"),
7769 #endif
7770 #if HAVE_NEWPAD
7771         CMDS(TRUE, ONLY(pad_test),      'p', "exercise pad features"),
7772 #endif
7773         CMDS(TRUE, ONLY(NULL),          'q', "quit"),
7774 #if USE_LIBMENU
7775         CMDS(TRUE, ONLY(form_test),     'r', "exercise form library"),
7776 #endif
7777 #if HAVE_COPYWIN
7778         CMDS(TRUE, BOTH(overlap_test),  's', "overlapping-refresh test"),
7779 #endif
7780 #if USE_LIBMENU && defined(TRACE)
7781         CMDS(TRUE, ONLY(trace_set),     't', "set trace level"),
7782 #endif
7783         CMDS(TRUE, ONLY(settings_test), 'v', "show terminal name and settings"),
7784         CMDS(FALSE, ONLY(NULL),         '?', "repeat this command summary")
7785     };
7786     /* *INDENT-ON* */
7787
7788     int (*doit) (bool);
7789     char command;
7790     unsigned n;
7791
7792     do {
7793         printf("This is the ncurses main menu (uppercase for wide-characters)\n");
7794         for (n = 0; n < SIZEOF(cmds); ++n) {
7795             if (top || cmds[n].recur) {
7796                 putchar(' ');
7797 #if USE_WIDEC_SUPPORT
7798                 if (cmds[n].wide_func) {
7799                     printf("%c,", toupper(cmds[n].code));
7800                 }
7801 #endif
7802                 printf("%c\t= %s\n", cmds[n].code, cmds[n].help);
7803             }
7804         }
7805
7806         (void) fputs("> ", stdout);
7807         (void) fflush(stdout);  /* necessary under SVr4 curses */
7808
7809         /*
7810          * This used to be an 'fgets()' call (until 1996/10).  However with
7811          * some runtime libraries, mixing stream I/O and 'read()' causes the
7812          * input stream to be flushed when switching between the two.
7813          */
7814         command = 0;
7815         for (;;) {
7816             char ch = '\0';
7817             if (read(fileno(stdin), &ch, (size_t) 1) <= 0) {
7818                 int save_err = errno;
7819                 perror("\nOOPS");
7820                 if (save_err == EINTR) {
7821                     clearerr(stdin);
7822                     continue;
7823                 } else if (command == 0) {
7824                     command = 'q';
7825                 }
7826                 break;
7827             } else if (command == 0 && !isspace(UChar(ch))) {
7828                 command = ch;
7829             } else if (ch == '\n' || ch == '\r') {
7830                 if ((command == 'd') && !top) {
7831                     (void) fputs("Do not nest test-d\n", stdout);
7832                     command = 0;
7833                 }
7834                 if (command != 0)
7835                     break;
7836                 (void) fputs("> ", stdout);
7837                 (void) fflush(stdout);
7838             }
7839         }
7840
7841         doit = NULL;
7842         for (n = 0; n < SIZEOF(cmds); ++n) {
7843             if (cmds[n].code == command) {
7844                 doit = cmds[n].narrow_func;
7845                 break;
7846             }
7847 #if USE_WIDEC_SUPPORT
7848             if (toupper(cmds[n].code) == command) {
7849                 doit = cmds[n].wide_func;
7850                 break;
7851             }
7852 #endif
7853         }
7854
7855         if (doit != NULL && doit(FALSE) == OK) {
7856             /*
7857              * This may be overkill; it's intended to reset everything back
7858              * to the initial terminal modes so that tests don't get in
7859              * each other's way.
7860              */
7861             flushinp();
7862             set_terminal_modes();
7863             reset_prog_mode();
7864             clear();
7865             refresh();
7866             endwin();
7867             if (command == '?') {
7868                 (void) puts("This is the ncurses capability tester.");
7869                 (void)
7870                     puts("You may select a test from the main menu by typing the");
7871                 (void)
7872                     puts("key letter of the choice (the letter to left of the =)");
7873                 (void)
7874                     puts("at the > prompt.  Type `q' to exit.");
7875             }
7876             continue;
7877         }
7878     } while
7879         (command != 'q');
7880 }
7881
7882 /*+-------------------------------------------------------------------------
7883         main(argc,argv)
7884 --------------------------------------------------------------------------*/
7885
7886 int
7887 main(int argc, char *argv[])
7888 {
7889     int c;
7890     int my_e_param = 1;
7891 #ifdef NCURSES_VERSION_PATCH
7892 #if HAVE_USE_DEFAULT_COLORS
7893     int default_fg = COLOR_WHITE;
7894     int default_bg = COLOR_BLACK;
7895     bool default_colors = FALSE;
7896 #if HAVE_ASSUME_DEFAULT_COLORS
7897     bool assumed_colors = FALSE;
7898 #endif
7899 #endif
7900 #endif
7901     bool monochrome = FALSE;
7902 #if HAVE_COLOR_CONTENT
7903     bool xterm_colors = FALSE;
7904     char *palette_file = 0;
7905 #endif
7906
7907     setlocale(LC_ALL, "");
7908
7909     while ((c = getopt(argc, argv, "a:dEe:fhmp:s:Tt:x")) != -1) {
7910         switch (c) {
7911 #ifdef NCURSES_VERSION_PATCH
7912 #if HAVE_USE_DEFAULT_COLORS
7913 #if HAVE_ASSUME_DEFAULT_COLORS
7914         case 'a':
7915             assumed_colors = TRUE;
7916             switch (sscanf(optarg, "%d,%d", &default_fg, &default_bg)) {
7917             case 0:
7918                 default_fg = COLOR_WHITE;
7919                 /* FALLTHRU */
7920             case 1:
7921                 default_bg = COLOR_BLACK;
7922                 break;
7923             }
7924             break;
7925 #endif
7926         case 'd':
7927             default_colors = TRUE;
7928             break;
7929 #endif
7930 #endif
7931 #if HAVE_USE_ENV
7932         case 'E':
7933             use_env(FALSE);
7934             break;
7935 #endif
7936         case 'e':
7937             my_e_param = atoi(optarg);
7938 #ifdef NCURSES_VERSION
7939             if (my_e_param > 3) /* allow extended layouts */
7940                 usage();
7941 #else
7942             if (my_e_param > 1)
7943                 usage();
7944 #endif
7945             break;
7946 #if HAVE_RIPOFFLINE
7947         case 'f':
7948             ripoffline(-1, rip_footer);
7949             break;
7950         case 'h':
7951             ripoffline(1, rip_header);
7952             break;
7953 #endif /* HAVE_RIPOFFLINE */
7954         case 'm':
7955             monochrome = TRUE;
7956             break;
7957 #if HAVE_COLOR_CONTENT
7958         case 'p':
7959             palette_file = optarg;
7960             break;
7961 #endif
7962 #if USE_LIBPANEL
7963         case 's':
7964             nap_msec = (int) atol(optarg);
7965             break;
7966 #endif
7967 #if defined(NCURSES_VERSION_PATCH) && (NCURSES_VERSION_PATCH >= 20120714) && !defined(_WIN32)
7968         case 'T':
7969             use_tioctl(TRUE);
7970             break;
7971 #endif
7972 #ifdef TRACE
7973         case 't':
7974             save_trace = (unsigned) strtol(optarg, 0, 0);
7975             break;
7976 #endif
7977 #if HAVE_COLOR_CONTENT
7978         case 'x':
7979             xterm_colors = TRUE;
7980             break;
7981 #endif
7982         default:
7983             usage();
7984         }
7985     }
7986
7987     /*
7988      * If there's no menus (unlikely for ncurses!), then we'll have to set
7989      * tracing on initially, just in case the user wants to test something that
7990      * doesn't involve wGetchar.
7991      */
7992 #ifdef TRACE
7993     /* enable debugging */
7994 #if !USE_LIBMENU
7995     trace(save_trace);
7996 #else
7997     if (!isatty(fileno(stdin)))
7998         trace(save_trace);
7999 #endif /* USE_LIBMENU */
8000 #endif /* TRACE */
8001
8002 #if USE_SOFTKEYS
8003     /* tell it we're going to play with soft keys */
8004     slk_init(my_e_param);
8005 #endif
8006
8007 #ifdef SIGUSR1
8008     /* set up null signal catcher so we can see what interrupts to getch do */
8009     signal(SIGUSR1, announce_sig);
8010 #endif
8011
8012     /* we must initialize the curses data structure only once */
8013     initscr();
8014     bkgdset(BLANK);
8015
8016     set_terminal_modes();
8017     def_prog_mode();
8018
8019     /* tests, in general, will want these modes */
8020     UseColors = (bool) (monochrome ? FALSE : has_colors());
8021
8022     if (UseColors) {
8023         start_color();
8024 #ifdef NCURSES_VERSION_PATCH
8025         MaxColors = COLORS;     /* was > 16 ? 16 : COLORS */
8026 #if HAVE_USE_DEFAULT_COLORS
8027         if (default_colors) {
8028             use_default_colors();
8029             MinColors = -1;
8030         }
8031 #if HAVE_ASSUME_DEFAULT_COLORS
8032         if (assumed_colors)
8033             assume_default_colors(default_fg, default_bg);
8034 #endif
8035 #endif
8036 #else /* normal SVr4 curses */
8037         MaxColors = COLORS;     /* was > 8 ? 8 : COLORS */
8038 #endif
8039         max_pairs = COLOR_PAIRS;        /* was > 256 ? 256 : COLOR_PAIRS */
8040
8041 #if HAVE_COLOR_CONTENT
8042         if (can_change_color()) {
8043             init_all_colors(xterm_colors, palette_file);
8044         }
8045 #endif
8046     }
8047
8048     /*
8049      * Return to terminal mode, so we're guaranteed of being able to
8050      * select terminal commands even if the capabilities are wrong.
8051      */
8052     endwin();
8053
8054 #if HAVE_CURSES_VERSION
8055     (void) printf("Welcome to %s.  Press ? for help.\n", curses_version());
8056 #elif defined(NCURSES_VERSION_MAJOR) && defined(NCURSES_VERSION_MINOR) && defined(NCURSES_VERSION_PATCH)
8057     (void) printf("Welcome to ncurses %d.%d.%d.  Press ? for help.\n",
8058                   NCURSES_VERSION_MAJOR,
8059                   NCURSES_VERSION_MINOR,
8060                   NCURSES_VERSION_PATCH);
8061 #else
8062     (void) puts("Welcome to ncurses.  Press ? for help.");
8063 #endif
8064
8065     main_menu(TRUE);
8066
8067     ExitProgram(EXIT_SUCCESS);
8068 }
8069
8070 /* ncurses.c ends here */