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