]> ncurses.scripts.mit.edu Git - ncurses.git/blob - test/ncurses.c
ncurses 6.4 - patch 20230128
[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);