• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /****************************************************************************
2  * Copyright 2018-2022,2023 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.538 2023/11/11 01:23:59 tom Exp $
45 
46 ***************************************************************************/
47 
48 #define NEED_TIME_H 1
49 #include <test.priv.h>
50 
51 #ifdef __hpux
52 #undef mvwdelch			/* HPUX 11.23 macro will not compile */
53 #endif
54 
55 #if HAVE_SYS_TIME_H && HAVE_SYS_TIME_SELECT
56 #include <sys/time.h>
57 #endif
58 
59 #if HAVE_SYS_SELECT_H
60 #include <sys/select.h>
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
failed(const char * s)172 failed(const char *s)
173 {
174     perror(s);
175     endwin();
176     ExitProgram(EXIT_FAILURE);
177 }
178 
179 static void
Repaint(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
isQuit(int c,bool escape)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
wGetchar(WINDOW * win)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
wGetstring(WINDOW * win,char * buffer,int limit)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
fullwidth_digit(int ch)298 fullwidth_digit(int ch)
299 {
300     return (wchar_t) (ch + 0xff10 - '0');
301 }
302 
303 static void
make_fullwidth_text(wchar_t * target,const char * source)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
make_narrow_text(wchar_t * target,const char * source)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
make_fullwidth_digit(cchar_t * target,int digit)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
wGet_wchar(WINDOW * win,wint_t * result)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
wGet_wstring(WINDOW * win,wchar_t * buffer,int limit)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
Pause(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
Cannot(const char * what)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
ShellOut(bool message)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 *
mouse_decode(MEVENT const * ep)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
show_mouse(WINDOW * win)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
setup_getch(WINDOW * win,GetchFlags flags)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
init_getch(WINDOW * win,GetchFlags flags,int delay)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
blocking_getch(GetchFlags flags,int delay)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
wgetch_help(WINDOW * win,GetchFlags flags)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
wgetch_wrap(WINDOW * win,int first_y)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
forget_boxes(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
remember_boxes(unsigned level,WINDOW * txt_win,WINDOW * box_win)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
slk_repaint(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
resize_boxes(unsigned level,const WINDOW * const win)773 resize_boxes(unsigned level, const WINDOW *const 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 *
ok_keyname(int code)813 ok_keyname(int code)
814 {
815     return ((code == OK) ? "OK" : ((code == ERR) ? "ERR" : keyname(code)));
816 }
817 
818 static void
wgetch_test(unsigned level,WINDOW * win,int delay)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
begin_getch_test(void)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
finish_getch_test(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
getch_test(bool recur GCC_UNUSED)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
resize_wide_boxes(unsigned level,const WINDOW * const win)1015 resize_wide_boxes(unsigned level, const WINDOW *const 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 *
wcstos(const wchar_t * src)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
wget_wch_test(unsigned level,WINDOW * win,int delay)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
x_getch_test(bool recur GCC_UNUSED)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
my_termattrs(void)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
attr_legend(WINDOW * helpwin)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
show_color_attr(int fg,int bg,int tx)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
cycle_color_attr(int ch,NCURSES_COLOR_T * fg,NCURSES_COLOR_T * bg,NCURSES_COLOR_T * tx)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
adjust_attr_string(int adjust)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
default_attr_string(void)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
init_attr_string(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
show_attr(WINDOW * win,int row,int skip,bool arrow,chtype attr,const char * name)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
init_attr_list(ATTR_TBL * target,attr_t attrs)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
init_w_attr_list(W_ATTR_TBL * target,attr_t attrs)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
attr_getc(int * skip,NCURSES_COLOR_T * fg,NCURSES_COLOR_T * bg,NCURSES_COLOR_T * tx,int * ac,unsigned * kc,unsigned limit)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
attr_test(bool recur GCC_UNUSED)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
normal_wchar(int ch)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
target_wchar(int ch)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
wide_adjust_attr_string(int adjust)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
wide_init_attr_string(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
set_wide_background(NCURSES_PAIRS_T pair)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
get_wide_background(void)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
wide_show_attr(WINDOW * win,int row,int skip,bool arrow,attr_t attr,NCURSES_PAIRS_T pair,const char * name)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
wide_attr_getc(int * skip,NCURSES_COLOR_T * fg,NCURSES_COLOR_T * bg,NCURSES_COLOR_T * tx,int * ac,unsigned * kc,unsigned limit)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
x_attr_test(bool recur GCC_UNUSED)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
show_color_name(int y,int x,int color,bool wide,int zoom)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
color_legend(WINDOW * helpwin,bool wide)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
color_cycle(int current,int step)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
color_test(bool recur GCC_UNUSED)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
x_color_test(bool recur GCC_UNUSED)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
change_color(NCURSES_PAIRS_T current,int field,int value,int usebase)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
reset_all_colors(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
init_all_colors(bool xterm_colors,char * palette_file)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
color_edit(bool recur GCC_UNUSED)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
cycle_attr(int ch,unsigned * at_code,attr_t * attr,ATTR_TBL * list,unsigned limit)3160 cycle_attr(int ch, unsigned *at_code, attr_t *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
cycle_w_attr(int ch,unsigned * at_code,attr_t * attr,W_ATTR_TBL * list,unsigned limit)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
cycle_colors(int ch,int * fg,int * bg,NCURSES_PAIRS_T * pair)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
slk_help(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
call_slk_color(int fg,int bg)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
slk_test(bool recur GCC_UNUSED)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     attr_t 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((chtype) 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
x_slk_test(bool recur GCC_UNUSED)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
show_256_chars(int repeat,attr_t attr,NCURSES_PAIRS_T pair)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
show_upper_chars(int base,int pagesize,int repeat,attr_t attr,NCURSES_PAIRS_T pair)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
show_pc_chars(int repeat,attr_t attr,NCURSES_PAIRS_T pair)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
show_box_chars(int repeat,attr_t attr,NCURSES_PAIRS_T pair)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
show_1_acs(int n,int repeat,const char * name,chtype code)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
show_acs_chars(int repeat,attr_t attr,NCURSES_PAIRS_T pair)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
acs_test(bool recur GCC_UNUSED)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     attr_t 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 *
merge_wide_attr(cchar_t * dst,const cchar_t * src,attr_t attr,NCURSES_PAIRS_T pair)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
show_paged_widechars(unsigned base,unsigned pagesize,int repeat,int space,attr_t attr,NCURSES_PAIRS_T pair)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
show_upper_widechars(unsigned first,int repeat,int space,attr_t attr,NCURSES_PAIRS_T pair)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
show_1_wacs(int n,int repeat,const char * name,const cchar_t * code)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
show_wacs_chars(int repeat,attr_t attr,NCURSES_PAIRS_T pair)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
show_wacs_chars_double(int repeat,attr_t attr,NCURSES_PAIRS_T pair)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
show_wacs_chars_thick(int repeat,attr_t attr,NCURSES_PAIRS_T pair)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
show_wbox_chars(int repeat,attr_t attr,NCURSES_PAIRS_T pair)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
show_2_wacs(int n,const char * name,const char * code,attr_t attr,NCURSES_PAIRS_T pair)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
show_utf8_chars(int repeat,attr_t attr,NCURSES_PAIRS_T pair)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
x_acs_test(bool recur GCC_UNUSED)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
sgr_attr_test(bool recur GCC_UNUSED)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 *
frame_win(FRAME * curp)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
HaveKeypad(FRAME * curp)4627 HaveKeypad(FRAME * curp)
4628 {
4629     WINDOW *win = frame_win(curp);
4630     (void) win;
4631     return is_keypad(win);
4632 }
4633 
4634 static bool
HaveScroll(FRAME * curp)4635 HaveScroll(FRAME * curp)
4636 {
4637     WINDOW *win = frame_win(curp);
4638     (void) win;
4639     return is_scrollok(win);
4640 }
4641 
4642 static void
newwin_legend(FRAME * curp)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
transient(FRAME * curp,NCURSES_CONST char * msg)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
newwin_report(FRAME * curp)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 *
selectcell(int uli,int ulj,int lri,int lrj)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
outerbox(pair ul,pair lr,bool onoff)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 *
getwindow(void)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
newwin_move(FRAME * curp,int dy,int dx)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 *
delete_framed(FRAME * fp,bool showit)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
scroll_test(bool recur GCC_UNUSED)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
wait_a_while(int msec GCC_UNUSED)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
saywhat(NCURSES_CONST char * text)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 *
mkpanel(NCURSES_COLOR_T color,int rows,int cols,int tly,int tlx)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
rmpanel(PANEL * pan)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
pflush(void)5231 pflush(void)
5232 {
5233     update_panels();
5234     doupdate();
5235 }				/* end of pflush */
5236 
5237 /*+-------------------------------------------------------------------------
5238 	fill_panel(win)
5239 --------------------------------------------------------------------------*/
5240 static void
init_panel(WINDOW * win)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
fill_panel(PANEL * pan)5252 fill_panel(PANEL *pan)
5253 {
5254     WINDOW *win = panel_window(pan);
5255     const char *userptr = (const char *) panel_userptr(pan);
5256     int num = (userptr && *userptr) ? userptr[1] : '?';
5257     int y, x;
5258 
5259     wmove(win, 1, 1);
5260     wprintw(win, "-pan%c-", num);
5261     wclrtoeol(win);
5262     box(win, 0, 0);
5263     for (y = 2; y < getmaxy(win) - 1; y++) {
5264 	for (x = 1; x < getmaxx(win) - 1; x++) {
5265 	    wmove(win, y, x);
5266 	    waddch(win, UChar(num));
5267 	}
5268     }
5269 }
5270 
5271 #if USE_WIDEC_SUPPORT
5272 static void
init_wide_panel(WINDOW * win)5273 init_wide_panel(WINDOW *win)
5274 {
5275     int digit;
5276     cchar_t temp[10];
5277 
5278     for (digit = 0; digit < 10; ++digit)
5279 	make_fullwidth_digit(&temp[digit], digit);
5280 
5281     do {
5282 	int y, x;
5283 	getyx(stdscr, y, x);
5284 	digit = (y + x / 2) % 10;
5285     } while (wadd_wch(win, &temp[digit]) != ERR);
5286 }
5287 
5288 static void
fill_wide_panel(PANEL * pan)5289 fill_wide_panel(PANEL *pan)
5290 {
5291     WINDOW *win = panel_window(pan);
5292     const char *userptr = (const char *) panel_userptr(pan);
5293     int num = (userptr && *userptr) ? userptr[1] : '?';
5294     int y, x;
5295 
5296     wmove(win, 1, 1);
5297     wprintw(win, "-pan%c-", num);
5298     wclrtoeol(win);
5299     box(win, 0, 0);
5300     for (y = 2; y < getmaxy(win) - 1; y++) {
5301 	for (x = 1; x < getmaxx(win) - 1; x++) {
5302 	    wmove(win, y, x);
5303 	    waddch(win, UChar(num));
5304 	}
5305     }
5306 }
5307 #endif
5308 
5309 #define MAX_PANELS 5
5310 
5311 static void
canned_panel(PANEL * px[MAX_PANELS+1],NCURSES_CONST char * cmd)5312 canned_panel(PANEL *px[MAX_PANELS + 1], NCURSES_CONST char *cmd)
5313 {
5314     int which = cmd[1] - '0';
5315 
5316     saywhat(cmd);
5317     switch (*cmd) {
5318     case 'h':
5319 	hide_panel(px[which]);
5320 	break;
5321     case 's':
5322 	show_panel(px[which]);
5323 	break;
5324     case 't':
5325 	top_panel(px[which]);
5326 	break;
5327     case 'b':
5328 	bottom_panel(px[which]);
5329 	break;
5330     case 'd':
5331 	rmpanel(px[which]);
5332 	break;
5333     }
5334     pflush();
5335     wait_a_while(nap_msec);
5336 }
5337 
5338 static int
demo_panels(void (* InitPanel)(WINDOW *),void (* FillPanel)(PANEL *))5339 demo_panels(void (*InitPanel) (WINDOW *), void (*FillPanel) (PANEL *))
5340 {
5341     int count;
5342     int itmp;
5343     PANEL *px[MAX_PANELS + 1];
5344 
5345     scrollok(stdscr, FALSE);	/* we don't want stdscr to scroll! */
5346     refresh();
5347 
5348     InitPanel(stdscr);
5349     for (count = 0; count < 5; count++) {
5350 	px[1] = mkpanel(COLOR_RED,
5351 			LINES / 2 - 2,
5352 			COLS / 8 + 1,
5353 			0,
5354 			0);
5355 	set_panel_userptr(px[1], (NCURSES_CONST void *) "p1");
5356 
5357 	px[2] = mkpanel(COLOR_GREEN,
5358 			LINES / 2 + 1,
5359 			COLS / 7,
5360 			LINES / 4,
5361 			COLS / 10);
5362 	set_panel_userptr(px[2], (NCURSES_CONST void *) "p2");
5363 
5364 	px[3] = mkpanel(COLOR_YELLOW,
5365 			LINES / 4,
5366 			COLS / 10,
5367 			LINES / 2,
5368 			COLS / 9);
5369 	set_panel_userptr(px[3], (NCURSES_CONST void *) "p3");
5370 
5371 	px[4] = mkpanel(COLOR_BLUE,
5372 			LINES / 2 - 2,
5373 			COLS / 8,
5374 			LINES / 2 - 2,
5375 			COLS / 3);
5376 	set_panel_userptr(px[4], (NCURSES_CONST void *) "p4");
5377 
5378 	px[5] = mkpanel(COLOR_MAGENTA,
5379 			LINES / 2 - 2,
5380 			COLS / 8,
5381 			LINES / 2,
5382 			COLS / 2 - 2);
5383 	set_panel_userptr(px[5], (NCURSES_CONST void *) "p5");
5384 
5385 	FillPanel(px[1]);
5386 	FillPanel(px[2]);
5387 	FillPanel(px[3]);
5388 	FillPanel(px[4]);
5389 	FillPanel(px[5]);
5390 
5391 	hide_panel(px[4]);
5392 	hide_panel(px[5]);
5393 	pflush();
5394 	saywhat("");
5395 	wait_a_while(nap_msec);
5396 
5397 	saywhat("h3 s1 s2 s4 s5");
5398 	move_panel(px[1], 0, 0);
5399 	hide_panel(px[3]);
5400 	show_panel(px[1]);
5401 	show_panel(px[2]);
5402 	show_panel(px[4]);
5403 	show_panel(px[5]);
5404 	pflush();
5405 	wait_a_while(nap_msec);
5406 
5407 	canned_panel(px, "s1");
5408 	canned_panel(px, "s2");
5409 
5410 	saywhat("m2");
5411 	move_panel(px[2], LINES / 3 + 1, COLS / 8);
5412 	pflush();
5413 	wait_a_while(nap_msec);
5414 
5415 	canned_panel(px, "s3");
5416 
5417 	saywhat("m3");
5418 	move_panel(px[3], LINES / 4 + 1, COLS / 15);
5419 	pflush();
5420 	wait_a_while(nap_msec);
5421 
5422 	canned_panel(px, "b3");
5423 	canned_panel(px, "s4");
5424 	canned_panel(px, "s5");
5425 	canned_panel(px, "t3");
5426 	canned_panel(px, "t1");
5427 	canned_panel(px, "t2");
5428 	canned_panel(px, "t3");
5429 	canned_panel(px, "t4");
5430 
5431 	for (itmp = 0; itmp < 6; itmp++) {
5432 	    WINDOW *w4 = panel_window(px[4]);
5433 	    WINDOW *w5 = panel_window(px[5]);
5434 
5435 	    saywhat("m4");
5436 	    wmove(w4, LINES / 8, 1);
5437 	    waddstr(w4, mod[itmp]);
5438 	    move_panel(px[4], LINES / 6, itmp * (COLS / 8));
5439 	    wmove(w5, LINES / 6, 1);
5440 	    waddstr(w5, mod[itmp]);
5441 	    pflush();
5442 	    wait_a_while(nap_msec);
5443 
5444 	    saywhat("m5");
5445 	    wmove(w4, LINES / 6, 1);
5446 	    waddstr(w4, mod[itmp]);
5447 	    move_panel(px[5], LINES / 3 - 1, (itmp * 10) + 6);
5448 	    wmove(w5, LINES / 8, 1);
5449 	    waddstr(w5, mod[itmp]);
5450 	    pflush();
5451 	    wait_a_while(nap_msec);
5452 	}
5453 
5454 	saywhat("m4");
5455 	move_panel(px[4], LINES / 6, itmp * (COLS / 8));
5456 	pflush();
5457 	wait_a_while(nap_msec);
5458 
5459 	canned_panel(px, "t5");
5460 	canned_panel(px, "t2");
5461 	canned_panel(px, "t1");
5462 	canned_panel(px, "d2");
5463 	canned_panel(px, "h3");
5464 	canned_panel(px, "d1");
5465 	canned_panel(px, "d4");
5466 	canned_panel(px, "d5");
5467 	canned_panel(px, "d3");
5468 
5469 	wait_a_while(nap_msec);
5470 	if (nap_msec == 1)
5471 	    break;
5472 	nap_msec = 100L;
5473     }
5474 
5475     erase();
5476     endwin();
5477     return OK;
5478 }
5479 
5480 #if USE_LIBPANEL
5481 static int
panel_test(bool recur GCC_UNUSED)5482 panel_test(bool recur GCC_UNUSED)
5483 {
5484     return demo_panels(init_panel, fill_panel);
5485 }
5486 #endif
5487 
5488 #if USE_WIDEC_SUPPORT && USE_LIBPANEL
5489 static int
x_panel_test(bool recur GCC_UNUSED)5490 x_panel_test(bool recur GCC_UNUSED)
5491 {
5492     return demo_panels(init_wide_panel, fill_wide_panel);
5493 }
5494 #endif
5495 #endif /* USE_LIBPANEL */
5496 
5497 /****************************************************************************
5498  *
5499  * Pad tester
5500  *
5501  ****************************************************************************/
5502 
5503 #if HAVE_NEWPAD
5504 
5505 /* The behavior of mvhline, mvvline for negative/zero length is unspecified,
5506  * though we can rely on negative x/y values to stop the macro.
5507  */
5508 static void
do_h_line(int y,int x,chtype c,int to)5509 do_h_line(int y, int x, chtype c, int to)
5510 {
5511     if ((to) > (x))
5512 	MvHLine(y, x, c, (to) - (x));
5513 }
5514 
5515 static void
do_v_line(int y,int x,chtype c,int to)5516 do_v_line(int y, int x, chtype c, int to)
5517 {
5518     if ((to) > (y))
5519 	MvVLine(y, x, c, (to) - (y));
5520 }
5521 
5522 #define GRIDSIZE	3
5523 
5524 static bool pending_pan = FALSE;
5525 static bool show_panner_legend = TRUE;
5526 
5527 static int
panner_legend(int line)5528 panner_legend(int line)
5529 {
5530     static const char *const legend[] =
5531     {
5532 	"Use arrow keys (or U,D,L,R) to pan, ESC to quit, ! to shell-out.",
5533 	"Use +,- (or j,k) to grow/shrink the panner vertically.",
5534 	"Use <,> (or h,l) to grow/shrink the panner horizontally.",
5535 	"Number repeats.  Toggle legend:? filler:a timer:t scrollmark:s."
5536     };
5537     int n = ((int) SIZEOF(legend) - (LINES - line));
5538     if (n >= 0 && n < (int) SIZEOF(legend)) {
5539 	if (move(line, 0) != ERR) {
5540 	    if (show_panner_legend)
5541 		printw("%s", legend[n]);
5542 	    clrtoeol();
5543 	    return show_panner_legend;
5544 	}
5545     }
5546     return FALSE;
5547 }
5548 
5549 static void
panner_h_cleanup(int from_y,int from_x,int to_x)5550 panner_h_cleanup(int from_y, int from_x, int to_x)
5551 {
5552     if (!panner_legend(from_y))
5553 	do_h_line(from_y, from_x, ' ', to_x);
5554 }
5555 
5556 static void
panner_v_cleanup(int from_y,int from_x,int to_y)5557 panner_v_cleanup(int from_y, int from_x, int to_y)
5558 {
5559     if (!panner_legend(from_y))
5560 	do_v_line(from_y, from_x, ' ', to_y);
5561 }
5562 
5563 static void
fill_pad(WINDOW * panpad,bool pan_lines,bool colored)5564 fill_pad(WINDOW *panpad, bool pan_lines, bool colored)
5565 {
5566     int y, x;
5567     unsigned gridcount = 0;
5568     chtype fill = 0;
5569 #ifdef A_COLOR
5570     if (colored)
5571 	fill = (chtype) COLOR_PAIR(1);
5572 #endif
5573 
5574     wmove(panpad, 0, 0);
5575     for (y = 0; y < getmaxy(panpad); y++) {
5576 	for (x = 0; x < getmaxx(panpad); x++) {
5577 	    if (y % GRIDSIZE == 0 && x % GRIDSIZE == 0) {
5578 		if (y == 0 && x == 0)
5579 		    waddch(panpad, pan_lines ? ACS_ULCORNER : '+');
5580 		else if (y == 0)
5581 		    waddch(panpad, pan_lines ? ACS_TTEE : '+');
5582 		else if (y == 0 || x == 0)
5583 		    waddch(panpad, pan_lines ? ACS_LTEE : '+');
5584 		else
5585 		    waddch(panpad, (chtype) ((pan_lines ? 'a' : 'A') +
5586 					     (int) (gridcount++ % 26)) | fill);
5587 	    } else if (y % GRIDSIZE == 0)
5588 		waddch(panpad, pan_lines ? ACS_HLINE : '-');
5589 	    else if (x % GRIDSIZE == 0)
5590 		waddch(panpad, pan_lines ? ACS_VLINE : '|');
5591 	    else
5592 		waddch(panpad, ' ');
5593 	}
5594     }
5595 }
5596 
5597 static void
panner(WINDOW * pad,int top_x,int top_y,int porty,int portx,int (* pgetc)(WINDOW *),bool colored)5598 panner(WINDOW *pad,
5599        int top_x, int top_y, int porty, int portx,
5600        int (*pgetc) (WINDOW *),
5601        bool colored)
5602 {
5603     TimeType before, after;
5604     bool timing = TRUE;
5605     bool pan_lines = FALSE;
5606     bool scrollers = TRUE;
5607     int basex = 0;
5608     int basey = 0;
5609     int pxmax, pymax, lowend, highend, c;
5610 
5611     getmaxyx(pad, pymax, pxmax);
5612     scrollok(stdscr, FALSE);	/* we don't want stdscr to scroll! */
5613 
5614     c = KEY_REFRESH;
5615     do {
5616 #ifdef NCURSES_VERSION
5617 	/*
5618 	 * During shell-out, the user may have resized the window.  Adjust
5619 	 * the port size of the pad to accommodate this.  Ncurses automatically
5620 	 * resizes all of the normal windows to fit on the new screen.
5621 	 */
5622 	if (top_x > COLS)
5623 	    top_x = COLS;
5624 	if (portx > COLS)
5625 	    portx = COLS;
5626 	if (top_y > LINES)
5627 	    top_y = LINES;
5628 	if (porty > LINES)
5629 	    porty = LINES;
5630 #endif
5631 	switch (c) {
5632 	case KEY_REFRESH:
5633 	    erase();
5634 
5635 	    /* FALLTHRU */
5636 	case HELP_KEY_1:
5637 	    if (c == HELP_KEY_1)
5638 		show_panner_legend = !show_panner_legend;
5639 	    panner_legend(LINES - 4);
5640 	    panner_legend(LINES - 3);
5641 	    panner_legend(LINES - 2);
5642 	    panner_legend(LINES - 1);
5643 	    break;
5644 	case 'a':
5645 	    pan_lines = !pan_lines;
5646 	    fill_pad(pad, pan_lines, colored);
5647 	    pending_pan = FALSE;
5648 	    break;
5649 
5650 	case 't':
5651 	    timing = !timing;
5652 	    if (!timing)
5653 		panner_legend(LINES - 1);
5654 	    break;
5655 	case 's':
5656 	    scrollers = !scrollers;
5657 	    break;
5658 
5659 	    /* Move the top-left corner of the pad, keeping the bottom-right
5660 	     * corner fixed.
5661 	     */
5662 	case 'h':		/* increase-columns: move left edge to left */
5663 	    if (top_x <= 0)
5664 		beep();
5665 	    else {
5666 		panner_v_cleanup(top_y, top_x, porty);
5667 		top_x--;
5668 	    }
5669 	    break;
5670 
5671 	case 'j':		/* decrease-lines: move top-edge down */
5672 	    if (top_y >= porty)
5673 		beep();
5674 	    else {
5675 		panner_h_cleanup(top_y - 1, top_x - (top_x > 0), portx);
5676 		top_y++;
5677 	    }
5678 	    break;
5679 
5680 	case 'k':		/* increase-lines: move top-edge up */
5681 	    if (top_y <= 0)
5682 		beep();
5683 	    else {
5684 		top_y--;
5685 		panner_h_cleanup(top_y, top_x, portx);
5686 	    }
5687 	    break;
5688 
5689 	case 'l':		/* decrease-columns: move left-edge to right */
5690 	    if (top_x >= portx)
5691 		beep();
5692 	    else {
5693 		panner_v_cleanup(top_y - (top_y > 0), top_x - 1, porty);
5694 		top_x++;
5695 	    }
5696 	    break;
5697 
5698 	    /* Move the bottom-right corner of the pad, keeping the top-left
5699 	     * corner fixed.
5700 	     */
5701 	case KEY_IC:		/* increase-columns: move right-edge to right */
5702 	    if (portx >= pxmax || portx >= COLS)
5703 		beep();
5704 	    else {
5705 		panner_v_cleanup(top_y - (top_y > 0), portx - 1, porty);
5706 		++portx;
5707 	    }
5708 	    break;
5709 
5710 	case KEY_IL:		/* increase-lines: move bottom-edge down */
5711 	    if (porty >= pymax || porty >= LINES)
5712 		beep();
5713 	    else {
5714 		panner_h_cleanup(porty - 1, top_x - (top_x > 0), portx);
5715 		++porty;
5716 	    }
5717 	    break;
5718 
5719 	case KEY_DC:		/* decrease-columns: move bottom edge up */
5720 	    if (portx <= top_x)
5721 		beep();
5722 	    else {
5723 		portx--;
5724 		panner_v_cleanup(top_y - (top_y > 0), portx, porty);
5725 	    }
5726 	    break;
5727 
5728 	case KEY_DL:		/* decrease-lines */
5729 	    if (porty <= top_y)
5730 		beep();
5731 	    else {
5732 		porty--;
5733 		panner_h_cleanup(porty, top_x - (top_x > 0), portx);
5734 	    }
5735 	    break;
5736 
5737 	case KEY_LEFT:		/* pan leftwards */
5738 	    if (basex > 0)
5739 		basex--;
5740 	    else
5741 		beep();
5742 	    break;
5743 
5744 	case KEY_RIGHT:	/* pan rightwards */
5745 	    if (basex + portx - (pymax > porty) < pxmax)
5746 		basex++;
5747 	    else
5748 		beep();
5749 	    break;
5750 
5751 	case KEY_UP:		/* pan upwards */
5752 	    if (basey > 0)
5753 		basey--;
5754 	    else
5755 		beep();
5756 	    break;
5757 
5758 	case KEY_DOWN:		/* pan downwards */
5759 	    if (basey + porty - (pxmax > portx) < pymax)
5760 		basey++;
5761 	    else
5762 		beep();
5763 	    break;
5764 
5765 	case 'H':
5766 	case KEY_HOME:
5767 	case KEY_FIND:
5768 	    basey = 0;
5769 	    break;
5770 
5771 	case 'E':
5772 	case KEY_END:
5773 	case KEY_SELECT:
5774 	    basey = pymax - porty;
5775 	    if (basey < 0)
5776 		basey = 0;
5777 	    break;
5778 
5779 	default:
5780 	    beep();
5781 	    break;
5782 	}
5783 
5784 	MvAddCh(top_y - 1, top_x - 1, ACS_ULCORNER);
5785 	do_v_line(top_y, top_x - 1, ACS_VLINE, porty);
5786 	do_h_line(top_y - 1, top_x, ACS_HLINE, portx);
5787 
5788 	if (scrollers && (pxmax > portx - 1)) {
5789 	    int length = (portx - top_x - 1);
5790 	    float ratio = ((float) length) / ((float) pxmax);
5791 
5792 	    lowend = (int) ((float) top_x + ((float) basex * ratio));
5793 	    highend = (int) ((float) top_x + ((float) (basex + length) * ratio));
5794 
5795 	    do_h_line(porty - 1, top_x, ACS_HLINE, lowend);
5796 	    if (highend < portx) {
5797 		attron(A_REVERSE);
5798 		do_h_line(porty - 1, lowend, ' ', highend + 1);
5799 		attroff(A_REVERSE);
5800 		do_h_line(porty - 1, highend + 1, ACS_HLINE, portx);
5801 	    }
5802 	} else
5803 	    do_h_line(porty - 1, top_x, ACS_HLINE, portx);
5804 
5805 	if (scrollers && (pymax > porty - 1)) {
5806 	    int length = (porty - top_y - 1);
5807 	    float ratio = ((float) length) / ((float) pymax);
5808 
5809 	    lowend = (int) ((float) top_y + ((float) basey * ratio));
5810 	    highend = (int) ((float) top_y + ((float) (basey + length) * ratio));
5811 
5812 	    do_v_line(top_y, portx - 1, ACS_VLINE, lowend);
5813 	    if (highend < porty) {
5814 		attron(A_REVERSE);
5815 		do_v_line(lowend, portx - 1, ' ', highend + 1);
5816 		attroff(A_REVERSE);
5817 		do_v_line(highend + 1, portx - 1, ACS_VLINE, porty);
5818 	    }
5819 	} else
5820 	    do_v_line(top_y, portx - 1, ACS_VLINE, porty);
5821 
5822 	MvAddCh(top_y - 1, portx - 1, ACS_URCORNER);
5823 	MvAddCh(porty - 1, top_x - 1, ACS_LLCORNER);
5824 	MvAddCh(porty - 1, portx - 1, ACS_LRCORNER);
5825 
5826 	if (!pending_pan) {
5827 	    GetClockTime(&before);
5828 	    wnoutrefresh(stdscr);
5829 
5830 	    pnoutrefresh(pad,
5831 			 basey, basex,
5832 			 top_y, top_x,
5833 			 porty - (pxmax > portx) - 1,
5834 			 portx - (pymax > porty) - 1);
5835 
5836 	    doupdate();
5837 	    if (timing) {
5838 		GetClockTime(&after);
5839 		move(LINES - 1, COLS - 12);
5840 		printw("Secs: %6.03f", ElapsedSeconds(&before, &after));
5841 		refresh();
5842 	    }
5843 	}
5844 
5845     } while
5846 	((c = pgetc(pad)) != KEY_EXIT);
5847 
5848     scrollok(stdscr, TRUE);	/* reset to driver's default */
5849 }
5850 
5851 static int
padgetch(WINDOW * win)5852 padgetch(WINDOW *win)
5853 {
5854     static int count;
5855     static int last;
5856 
5857     if ((pending_pan = (count > 0)) != FALSE) {
5858 	count--;
5859 	pending_pan = (count != 0);
5860     } else {
5861 	for (;;) {
5862 	    int c;
5863 	    switch (c = wGetchar(win)) {
5864 	    case '!':
5865 		ShellOut(FALSE);
5866 		/* FALLTHRU */
5867 	    case CTRL('r'):
5868 		endwin();
5869 		refresh();
5870 		c = KEY_REFRESH;
5871 		break;
5872 	    case CTRL('l'):
5873 		c = KEY_REFRESH;
5874 		break;
5875 	    case 'U':
5876 		c = KEY_UP;
5877 		break;
5878 	    case 'D':
5879 		c = KEY_DOWN;
5880 		break;
5881 	    case 'R':
5882 		c = KEY_RIGHT;
5883 		break;
5884 	    case 'L':
5885 		c = KEY_LEFT;
5886 		break;
5887 	    case '+':
5888 		c = KEY_IL;
5889 		break;
5890 	    case '-':
5891 		c = KEY_DL;
5892 		break;
5893 	    case '>':
5894 		c = KEY_IC;
5895 		break;
5896 	    case '<':
5897 		c = KEY_DC;
5898 		break;
5899 	    case ERR:		/* FALLTHRU */
5900 	    case case_QUIT:
5901 		count = 0;
5902 		c = KEY_EXIT;
5903 		break;
5904 	    default:
5905 		if (c >= '0' && c <= '9') {
5906 		    count = count * 10 + (c - '0');
5907 		    continue;
5908 		}
5909 		break;
5910 	    }
5911 	    last = c;
5912 	    break;
5913 	}
5914 	if (count > 0)
5915 	    count--;
5916     }
5917     return (last);
5918 }
5919 
5920 #define PAD_HIGH 200
5921 #define PAD_WIDE 200
5922 
5923 static int
pad_test(bool recur GCC_UNUSED)5924 pad_test(bool recur GCC_UNUSED)
5925 /* Demonstrate pads. */
5926 {
5927     WINDOW *panpad = newpad(PAD_HIGH, PAD_WIDE);
5928 
5929     if (panpad == 0) {
5930 	Cannot("cannot create requested pad");
5931 	return ERR;
5932     }
5933 #ifdef A_COLOR
5934     if (UseColors) {
5935 	init_pair(1, COLOR_BLACK, COLOR_GREEN);
5936 	init_pair(2, COLOR_CYAN, COLOR_BLUE);
5937 	wbkgd(panpad, (chtype) (COLOR_PAIR(2) | ' '));
5938     }
5939 #endif
5940     fill_pad(panpad, FALSE, TRUE);
5941 
5942     panner_legend(LINES - 4);
5943     panner_legend(LINES - 3);
5944     panner_legend(LINES - 2);
5945     panner_legend(LINES - 1);
5946 
5947     keypad(panpad, TRUE);
5948 
5949     /* Make the pad (initially) narrow enough that a trace file won't wrap.
5950      * We'll still be able to widen it during a test, since that's required
5951      * for testing boundaries.
5952      */
5953     panner(panpad, 2, 2, LINES - 5, COLS - 15, padgetch, TRUE);
5954 
5955     delwin(panpad);
5956     endwin();
5957     erase();
5958     return OK;
5959 }
5960 #endif /* HAVE_NEWPAD */
5961 
5962 /****************************************************************************
5963  *
5964  * Tests from John Burnell's PDCurses tester
5965  *
5966  ****************************************************************************/
5967 
5968 static void
Continue(WINDOW * win)5969 Continue(WINDOW *win)
5970 {
5971     noecho();
5972     wmove(win, 10, 1);
5973     MvWAddStr(win, 10, 1, " Press any key to continue");
5974     wrefresh(win);
5975     wGetchar(win);
5976 }
5977 
5978 static int
flushinp_test(bool recur GCC_UNUSED)5979 flushinp_test(bool recur GCC_UNUSED)
5980 /* Input test, adapted from John Burnell's PDCurses tester */
5981 {
5982     WINDOW *win = stdscr;
5983     int w, h, bx, by, sw, sh, i;
5984 
5985     WINDOW *subWin;
5986     wclear(win);
5987 
5988     getmaxyx(win, h, w);
5989     getbegyx(win, by, bx);
5990     sw = w / 3;
5991     sh = h / 3;
5992     if ((subWin = subwin(win, sh, sw, by + h - sh - 2, bx + w - sw - 2)) == 0)
5993 	return ERR;
5994 
5995 #ifdef A_COLOR
5996     if (UseColors) {
5997 	init_pair(2, COLOR_CYAN, COLOR_BLUE);
5998 	wbkgd(subWin, (chtype) (COLOR_PAIR(2) | ' '));
5999     }
6000 #endif
6001     (void) wattrset(subWin, A_BOLD);
6002     box(subWin, ACS_VLINE, ACS_HLINE);
6003     MvWAddStr(subWin, 2, 1, "This is a subwindow");
6004     wrefresh(win);
6005 
6006     /*
6007      * This used to set 'nocbreak()'.  However, Alexander Lukyanov says that
6008      * it only happened to "work" on SVr4 because that implementation does not
6009      * emulate nocbreak+noecho mode, whereas ncurses does.  To get the desired
6010      * test behavior, we're using 'cbreak()', which will allow a single
6011      * character to return without needing a newline. - T.Dickey 1997/10/11.
6012      */
6013     cbreak();
6014     MvWAddStr(win, 0, 1, "This is a test of the flushinp() call.");
6015 
6016     MvWAddStr(win, 2, 1, "Type random keys for 5 seconds.");
6017     MvWAddStr(win, 3, 1,
6018 	      "These should be discarded (not echoed) after the subwindow goes away.");
6019     wrefresh(win);
6020 
6021     for (i = 0; i < 5; i++) {
6022 	MvWPrintw(subWin, 1, 1, "Time = %d", i);
6023 	wrefresh(subWin);
6024 	napms(1000);
6025 	flushinp();
6026     }
6027 
6028     delwin(subWin);
6029     werase(win);
6030     flash();
6031     wrefresh(win);
6032     napms(1000);
6033 
6034     MvWAddStr(win, 2, 1,
6035 	      "If you were still typing when the window timer expired,");
6036     MvWAddStr(win, 3, 1,
6037 	      "or else you typed nothing at all while it was running,");
6038     MvWAddStr(win, 4, 1,
6039 	      "test was invalid.  You'll see garbage or nothing at all. ");
6040     MvWAddStr(win, 6, 1, "Press a key");
6041     wmove(win, 9, 10);
6042     wrefresh(win);
6043     echo();
6044     wGetchar(win);
6045     flushinp();
6046     MvWAddStr(win, 12, 0,
6047 	      "If you see any key other than what you typed, flushinp() is broken.");
6048     Continue(win);
6049 
6050     wmove(win, 9, 10);
6051     wdelch(win);
6052     wrefresh(win);
6053     wmove(win, 12, 0);
6054     clrtoeol();
6055     waddstr(win,
6056 	    "What you typed should now have been deleted; if not, wdelch() failed.");
6057     Continue(win);
6058 
6059     cbreak();
6060     return OK;
6061 }
6062 
6063 /****************************************************************************
6064  *
6065  * Menu test
6066  *
6067  ****************************************************************************/
6068 
6069 #if USE_LIBMENU
6070 
6071 #define MENU_Y	8
6072 #define MENU_X	8
6073 
6074 static int
menu_virtualize(int c)6075 menu_virtualize(int c)
6076 {
6077     if (c == '\n' || c == KEY_EXIT)
6078 	return (MAX_COMMAND + 1);
6079     else if (c == 'u')
6080 	return (REQ_SCR_ULINE);
6081     else if (c == 'd')
6082 	return (REQ_SCR_DLINE);
6083     else if (c == 'b' || c == KEY_NPAGE)
6084 	return (REQ_SCR_UPAGE);
6085     else if (c == 'f' || c == KEY_PPAGE)
6086 	return (REQ_SCR_DPAGE);
6087     else if (c == 'n' || c == KEY_DOWN)
6088 	return (REQ_NEXT_ITEM);
6089     else if (c == 'p' || c == KEY_UP)
6090 	return (REQ_PREV_ITEM);
6091     else if (c == ' ')
6092 	return (REQ_TOGGLE_ITEM);
6093     else {
6094 	if (c != KEY_MOUSE)
6095 	    beep();
6096 	return (c);
6097     }
6098 }
6099 
6100 static CONST_MENUS char *animals[] =
6101 {
6102     "Lions",
6103     "Tigers",
6104     "Bears",
6105     "(Oh my!)",
6106     "Newts",
6107     "Platypi",
6108     "Lemurs",
6109     "(Oh really?!)",
6110     "Leopards",
6111     "Panthers",
6112     "Pumas",
6113     "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
6114     "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs, Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
6115     (char *) 0
6116 };
6117 
6118 static int
menu_test(bool recur GCC_UNUSED)6119 menu_test(bool recur GCC_UNUSED)
6120 {
6121     MENU *m;
6122     ITEM *items[SIZEOF(animals)];
6123     ITEM **ip = items;
6124     CONST_MENUS char **ap;
6125     int mrows, mcols, c;
6126     WINDOW *menuwin;
6127 
6128 #ifdef NCURSES_MOUSE_VERSION
6129     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
6130 #endif
6131     MvAddStr(0, 0, "This is the menu test:");
6132     MvAddStr(2, 0, "  Use up and down arrow to move the select bar.");
6133     MvAddStr(3, 0, "  'n' and 'p' act like arrows.");
6134     MvAddStr(4, 0,
6135 	     "  'b' and 'f' scroll up/down (page), 'u' and 'd' (line).");
6136     MvAddStr(5, 0, "  Press return to exit.");
6137     refresh();
6138 
6139     for (ap = animals; *ap; ap++) {
6140 	if ((*ip = new_item(*ap, "")) != 0)
6141 	    ++ip;
6142     }
6143     *ip = (ITEM *) 0;
6144 
6145     m = new_menu(items);
6146 
6147     set_menu_format(m, (SIZEOF(animals) + 1) / 2, 1);
6148     scale_menu(m, &mrows, &mcols);
6149 
6150     menuwin = newwin(mrows + 2, mcols + 2, MENU_Y, MENU_X);
6151     set_menu_win(m, menuwin);
6152     keypad(menuwin, TRUE);
6153     box(menuwin, 0, 0);
6154 
6155     set_menu_sub(m, derwin(menuwin, mrows, mcols, 1, 1));
6156 
6157     post_menu(m);
6158 
6159     while ((c = menu_driver(m, menu_virtualize(wGetchar(menuwin)))) != E_UNKNOWN_COMMAND) {
6160 	if (c == E_NOT_POSTED)
6161 	    break;
6162 	if (c == E_REQUEST_DENIED)
6163 	    beep();
6164     }
6165 
6166     MvPrintw(LINES - 2, 0,
6167 	     "You chose: %s\n", item_name(current_item(m)));
6168     (void) addstr("Press any key to continue...");
6169     wGetchar(stdscr);
6170 
6171     unpost_menu(m);
6172     delwin(menuwin);
6173 
6174     free_menu(m);
6175     for (ip = items; *ip; ip++)
6176 	free_item(*ip);
6177 #ifdef NCURSES_MOUSE_VERSION
6178     mousemask(0, (mmask_t *) 0);
6179 #endif
6180     return OK;
6181 }
6182 
6183 #ifdef TRACE
6184 #define T_TBL(name) { #name, name }
6185 static struct {
6186     const char *name;
6187     unsigned mask;
6188 } t_tbl[] = {
6189 
6190     T_TBL(TRACE_DISABLE),
6191 	T_TBL(TRACE_TIMES),
6192 	T_TBL(TRACE_TPUTS),
6193 	T_TBL(TRACE_UPDATE),
6194 	T_TBL(TRACE_MOVE),
6195 	T_TBL(TRACE_CHARPUT),
6196 	T_TBL(TRACE_ORDINARY),
6197 	T_TBL(TRACE_CALLS),
6198 	T_TBL(TRACE_VIRTPUT),
6199 	T_TBL(TRACE_IEVENT),
6200 	T_TBL(TRACE_BITS),
6201 	T_TBL(TRACE_ICALLS),
6202 	T_TBL(TRACE_CCALLS),
6203 	T_TBL(TRACE_DATABASE),
6204 	T_TBL(TRACE_ATTRS),
6205 	T_TBL(TRACE_MAXIMUM),
6206     {
6207 	(char *) 0, 0
6208     }
6209 };
6210 
6211 static char *
tracetrace(unsigned tlevel)6212 tracetrace(unsigned tlevel)
6213 {
6214     static char *buf;
6215     static size_t need = 12;
6216     int n;
6217 
6218     if (buf == 0) {
6219 	for (n = 0; t_tbl[n].name != 0; n++)
6220 	    need += strlen(t_tbl[n].name) + 2;
6221 	buf = typeMalloc(char, need);
6222 	if (!buf)
6223 	    failed("tracetrace");
6224     }
6225     _nc_SPRINTF(buf, _nc_SLIMIT(need) "0x%02x = {", tlevel);
6226     if (tlevel == 0) {
6227 	_nc_STRCAT(buf, t_tbl[0].name ? t_tbl[0].name : "", need);
6228 	_nc_STRCAT(buf, ", ", need);
6229     } else {
6230 	for (n = 1; t_tbl[n].name != 0; n++)
6231 	    if ((tlevel & t_tbl[n].mask) == t_tbl[n].mask) {
6232 		_nc_STRCAT(buf, t_tbl[n].name, need);
6233 		_nc_STRCAT(buf, ", ", need);
6234 	    }
6235     }
6236     if (buf[strlen(buf) - 2] == ',')
6237 	buf[strlen(buf) - 2] = '\0';
6238     _nc_STRCAT(buf, "}", need);
6239     return buf;
6240 }
6241 
6242 /* fake a dynamically reconfigurable menu using the 0th entry to deselect
6243  * the others
6244  */
6245 static int
run_trace_menu(MENU * m)6246 run_trace_menu(MENU * m)
6247 {
6248     ITEM **items;
6249     ITEM *i, **p;
6250 
6251     for (;;) {
6252 	bool changed = FALSE;
6253 	switch (menu_driver(m, menu_virtualize(wGetchar(menu_win(m))))) {
6254 	case E_UNKNOWN_COMMAND:
6255 	    return FALSE;
6256 	default:
6257 	    items = menu_items(m);
6258 	    i = current_item(m);
6259 	    if (i == items[0]) {
6260 		if (item_value(i)) {
6261 		    for (p = items + 1; *p != 0; p++)
6262 			if (item_value(*p)) {
6263 			    set_item_value(*p, FALSE);
6264 			    changed = TRUE;
6265 			}
6266 		}
6267 	    } else {
6268 		for (p = items + 1; *p != 0; p++)
6269 		    if (item_value(*p)) {
6270 			set_item_value(items[0], FALSE);
6271 			changed = TRUE;
6272 			break;
6273 		    }
6274 	    }
6275 	    if (!changed)
6276 		return TRUE;
6277 	}
6278     }
6279 }
6280 
6281 static int
trace_set(bool recur GCC_UNUSED)6282 trace_set(bool recur GCC_UNUSED)
6283 /* interactively set the trace level */
6284 {
6285     MENU *m;
6286     ITEM *items[SIZEOF(t_tbl)];
6287     ITEM **ip = items;
6288     int mrows, mcols;
6289     unsigned newtrace;
6290     int n;
6291     WINDOW *menuwin;
6292 
6293     MvAddStr(0, 0, "Interactively set trace level:");
6294     MvAddStr(2, 0, "  Press space bar to toggle a selection.");
6295     MvAddStr(3, 0, "  Use up and down arrow to move the select bar.");
6296     MvAddStr(4, 0, "  Press return to set the trace level.");
6297     MvPrintw(6, 0, "(Current trace level is %s)", tracetrace(_nc_tracing));
6298 
6299     refresh();
6300 
6301     for (n = 0; t_tbl[n].name != 0; n++) {
6302 	if ((*ip = new_item(t_tbl[n].name, "")) != 0) {
6303 	    ++ip;
6304 	}
6305     }
6306     *ip = (ITEM *) 0;
6307 
6308     m = new_menu(items);
6309 
6310     set_menu_format(m, 0, 2);
6311     scale_menu(m, &mrows, &mcols);
6312 
6313     menu_opts_off(m, O_ONEVALUE);
6314     menuwin = newwin(mrows + 2, mcols + 2, MENU_Y, MENU_X);
6315     set_menu_win(m, menuwin);
6316     keypad(menuwin, TRUE);
6317     box(menuwin, 0, 0);
6318 
6319     set_menu_sub(m, derwin(menuwin, mrows, mcols, 1, 1));
6320 
6321     post_menu(m);
6322 
6323     for (ip = menu_items(m); *ip; ip++) {
6324 	unsigned mask = t_tbl[item_index(*ip)].mask;
6325 	if (mask == 0)
6326 	    set_item_value(*ip, _nc_tracing == 0);
6327 	else if ((mask & _nc_tracing) == mask)
6328 	    set_item_value(*ip, TRUE);
6329     }
6330 
6331     while (run_trace_menu(m)) {
6332 	/* EMPTY */ ;
6333     }
6334 
6335     newtrace = 0;
6336     for (ip = menu_items(m); *ip; ip++)
6337 	if (item_value(*ip))
6338 	    newtrace |= t_tbl[item_index(*ip)].mask;
6339     curses_trace(newtrace);
6340     Trace(("trace level interactively set to %s", tracetrace(_nc_tracing)));
6341 
6342     MvPrintw(LINES - 2, 0,
6343 	     "Trace level is %s\n", tracetrace(_nc_tracing));
6344     (void) addstr("Press any key to continue...");
6345     wGetchar(stdscr);
6346 
6347     unpost_menu(m);
6348     delwin(menuwin);
6349 
6350     free_menu(m);
6351     for (ip = items; *ip; ip++)
6352 	free_item(*ip);
6353 
6354     return OK;
6355 }
6356 #endif /* TRACE */
6357 #endif /* USE_LIBMENU */
6358 
6359 /****************************************************************************
6360  *
6361  * Forms test
6362  *
6363  ****************************************************************************/
6364 #if USE_LIBFORM
6365 static FIELD *
make_label(int frow,int fcol,NCURSES_CONST char * label)6366 make_label(int frow, int fcol, NCURSES_CONST char *label)
6367 {
6368     FIELD *f = new_field(1, (int) strlen(label), frow, fcol, 0, 0);
6369 
6370     if (f) {
6371 	set_field_buffer(f, 0, label);
6372 	set_field_opts(f, (int) ((unsigned) field_opts(f) & (unsigned) ~O_ACTIVE));
6373     }
6374     return (f);
6375 }
6376 
6377 static FIELD *
make_field(int frow,int fcol,int rows,int cols,bool secure)6378 make_field(int frow, int fcol, int rows, int cols, bool secure)
6379 {
6380     FIELD *f = new_field(rows, cols, frow, fcol, 0, secure ? 1 : 0);
6381 
6382     if (f) {
6383 	set_field_back(f, A_UNDERLINE);
6384 	set_field_userptr(f, (void *) 0);
6385     }
6386     return (f);
6387 }
6388 
6389 static void
display_form(FORM * f)6390 display_form(FORM *f)
6391 {
6392     WINDOW *w;
6393     int rows, cols;
6394 
6395     scale_form(f, &rows, &cols);
6396 
6397     if ((w = newwin(rows + 2, cols + 4, 0, 0)) != (WINDOW *) 0) {
6398 	set_form_win(f, w);
6399 	set_form_sub(f, derwin(w, rows, cols, 1, 2));
6400 	box(w, 0, 0);
6401 	keypad(w, TRUE);
6402 	if (post_form(f) != E_OK)
6403 	    wrefresh(w);
6404     }
6405 }
6406 
6407 static void
erase_form(FORM * f)6408 erase_form(FORM *f)
6409 {
6410     WINDOW *w = form_win(f);
6411     WINDOW *s = form_sub(f);
6412 
6413     unpost_form(f);
6414     werase(w);
6415     wrefresh(w);
6416     delwin(s);
6417     delwin(w);
6418 }
6419 
6420 static int
edit_secure(FIELD * me,int c)6421 edit_secure(FIELD *me, int c)
6422 {
6423     int rows, cols, frow, fcol, nrow, nbuf;
6424 
6425     if (field_info(me, &rows, &cols, &frow, &fcol, &nrow, &nbuf) == E_OK
6426 	&& nbuf > 0) {
6427 	char *source = field_buffer(me, 1);
6428 	size_t have = (source ? strlen(source) : 0) + 1;
6429 	size_t need = 80 + have;
6430 	char *temp = malloc(need);
6431 
6432 	if (temp != 0) {
6433 	    size_t len;
6434 	    _nc_STRNCPY(temp, source ? source : "", have + 1);
6435 	    len = (size_t) (char *) field_userptr(me);
6436 	    if (c <= KEY_MAX) {
6437 		if (isgraph(c) && (len + 1) < sizeof(temp)) {
6438 		    temp[len++] = (char) c;
6439 		    temp[len] = 0;
6440 		    set_field_buffer(me, 1, temp);
6441 		    c = '*';
6442 		} else {
6443 		    c = 0;
6444 		}
6445 	    } else {
6446 		switch (c) {
6447 		case REQ_BEG_FIELD:
6448 		case REQ_CLR_EOF:
6449 		case REQ_CLR_EOL:
6450 		case REQ_DEL_LINE:
6451 		case REQ_DEL_WORD:
6452 		case REQ_DOWN_CHAR:
6453 		case REQ_END_FIELD:
6454 		case REQ_INS_CHAR:
6455 		case REQ_INS_LINE:
6456 		case REQ_LEFT_CHAR:
6457 		case REQ_NEW_LINE:
6458 		case REQ_NEXT_WORD:
6459 		case REQ_PREV_WORD:
6460 		case REQ_RIGHT_CHAR:
6461 		case REQ_UP_CHAR:
6462 		    c = 0;	/* we don't want to do inline editing */
6463 		    break;
6464 		case REQ_CLR_FIELD:
6465 		    if (len) {
6466 			temp[0] = 0;
6467 			set_field_buffer(me, 1, temp);
6468 		    }
6469 		    break;
6470 		case REQ_DEL_CHAR:
6471 		case REQ_DEL_PREV:
6472 		    if (len) {
6473 			temp[--len] = 0;
6474 			set_field_buffer(me, 1, temp);
6475 		    }
6476 		    break;
6477 		}
6478 	    }
6479 	    set_field_userptr(me, (void *) len);
6480 	    free(temp);
6481 	}
6482     }
6483     return c;
6484 }
6485 
6486 static int
form_virtualize(FORM * f,WINDOW * w)6487 form_virtualize(FORM *f, WINDOW *w)
6488 {
6489     /* *INDENT-OFF* */
6490     static const struct {
6491 	int code;
6492 	int result;
6493     } lookup[] = {
6494 	{ CTRL('A'),	REQ_NEXT_CHOICE },
6495 	{ CTRL('B'),	REQ_PREV_WORD },
6496 	{ CTRL('C'),	REQ_CLR_EOL },
6497 	{ CTRL('D'),	REQ_DOWN_FIELD },
6498 	{ CTRL('E'),	REQ_END_FIELD },
6499 	{ CTRL('F'),	REQ_NEXT_PAGE },
6500 	{ CTRL('G'),	REQ_DEL_WORD },
6501 	{ CTRL('H'),	REQ_DEL_PREV },
6502 	{ CTRL('I'),	REQ_INS_CHAR },
6503 	{ CTRL('K'),	REQ_CLR_EOF },
6504 	{ CTRL('L'),	REQ_LEFT_FIELD },
6505 	{ CTRL('M'),	REQ_NEW_LINE },
6506 	{ CTRL('N'),	REQ_NEXT_FIELD },
6507 	{ CTRL('O'),	REQ_INS_LINE },
6508 	{ CTRL('P'),	REQ_PREV_FIELD },
6509 	{ CTRL('R'),	REQ_RIGHT_FIELD },
6510 	{ CTRL('S'),	REQ_BEG_FIELD },
6511 	{ CTRL('U'),	REQ_UP_FIELD },
6512 	{ CTRL('V'),	REQ_DEL_CHAR },
6513 	{ CTRL('W'),	REQ_NEXT_WORD },
6514 	{ CTRL('X'),	REQ_CLR_FIELD },
6515 	{ CTRL('Y'),	REQ_DEL_LINE },
6516 	{ CTRL('Z'),	REQ_PREV_CHOICE },
6517 	{ ESCAPE,	MAX_FORM_COMMAND + 1 },
6518 	{ KEY_BACKSPACE, REQ_DEL_PREV },
6519 	{ KEY_DOWN,	REQ_DOWN_CHAR },
6520 	{ KEY_END,	REQ_LAST_FIELD },
6521 	{ KEY_HOME,	REQ_FIRST_FIELD },
6522 	{ KEY_LEFT,	REQ_LEFT_CHAR },
6523 	{ KEY_LL,	REQ_LAST_FIELD },
6524 	{ KEY_NEXT,	REQ_NEXT_FIELD },
6525 	{ KEY_NPAGE,	REQ_NEXT_PAGE },
6526 	{ KEY_PPAGE,	REQ_PREV_PAGE },
6527 	{ KEY_PREVIOUS, REQ_PREV_FIELD },
6528 	{ KEY_RIGHT,	REQ_RIGHT_CHAR },
6529 	{ KEY_UP,	REQ_UP_CHAR },
6530 	{ QUIT,		MAX_FORM_COMMAND + 1 }
6531     };
6532     /* *INDENT-ON* */
6533 
6534     static int mode = REQ_INS_MODE;
6535     int c = wGetchar(w);
6536     FIELD *me = current_field(f);
6537     bool current = TRUE;
6538 
6539     if (c == CTRL(']')) {
6540 	if (mode == REQ_INS_MODE) {
6541 	    mode = REQ_OVL_MODE;
6542 	} else {
6543 	    mode = REQ_INS_MODE;
6544 	}
6545 	c = mode;
6546     } else {
6547 	unsigned n;
6548 	for (n = 0; n < SIZEOF(lookup); n++) {
6549 	    if (lookup[n].code == c) {
6550 		c = lookup[n].result;
6551 		break;
6552 	    }
6553 	}
6554     }
6555     MvPrintw(0, COLS - 6, "(%s)", mode == REQ_INS_MODE ? "INS" : "OVL");
6556 
6557     /*
6558      * Force the field that the user is typing into to be in reverse video,
6559      * while the other fields are shown underlined.
6560      */
6561     switch (c) {
6562     case REQ_BEG_FIELD:
6563     case REQ_CLR_EOF:
6564     case REQ_CLR_EOL:
6565     case REQ_CLR_FIELD:
6566     case REQ_DEL_CHAR:
6567     case REQ_DEL_LINE:
6568     case REQ_DEL_PREV:
6569     case REQ_DEL_WORD:
6570     case REQ_END_FIELD:
6571     case REQ_INS_CHAR:
6572     case REQ_INS_LINE:
6573     case REQ_LEFT_CHAR:
6574     case REQ_LEFT_FIELD:
6575     case REQ_NEXT_WORD:
6576     case REQ_RIGHT_CHAR:
6577 	current = TRUE;
6578 	break;
6579     default:
6580 	current = (c < KEY_MAX);
6581 	break;
6582     }
6583     if (current) {
6584 	c = edit_secure(me, c);
6585 	set_field_back(me, A_REVERSE);
6586     } else {
6587 	c = edit_secure(me, c);
6588 	set_field_back(me, A_UNDERLINE);
6589     }
6590     return c;
6591 }
6592 
6593 static int
my_form_driver(FORM * form,int c)6594 my_form_driver(FORM *form, int c)
6595 {
6596     if (c == (MAX_FORM_COMMAND + 1)
6597 	&& form_driver(form, REQ_VALIDATION) == E_OK)
6598 	return (TRUE);
6599     else {
6600 	beep();
6601 	return (FALSE);
6602     }
6603 }
6604 
6605 #ifdef NCURSES_VERSION
6606 #define FIELDCHECK_CB(func) bool func(FIELD * fld, const void * data GCC_UNUSED)
6607 #define CHAR_CHECK_CB(func) bool func(int ch, const void *data GCC_UNUSED)
6608 #else
6609 #define FIELDCHECK_CB(func) int func(FIELD * fld, char * data GCC_UNUSED)
6610 #define CHAR_CHECK_CB(func) int func(int ch, char *data GCC_UNUSED)
6611 #endif
6612 
6613 /*
6614  * Allow a middle initial, optionally with a '.' to end it.
6615  */
6616 static
FIELDCHECK_CB(mi_field_check)6617 FIELDCHECK_CB(mi_field_check)
6618 {
6619     char *s = field_buffer(fld, 0);
6620     int state = 0;
6621     int n;
6622 
6623     for (n = 0; s[n] != '\0'; ++n) {
6624 	switch (state) {
6625 	case 0:
6626 	    if (s[n] == '.') {
6627 		if (n != 1)
6628 		    return FALSE;
6629 		state = 2;
6630 	    } else if (isspace(UChar(s[n]))) {
6631 		state = 2;
6632 	    }
6633 	    break;
6634 	case 2:
6635 	    if (!isspace(UChar(s[n])))
6636 		return FALSE;
6637 	    break;
6638 	}
6639     }
6640 
6641     /* force the form to display a leading capital */
6642     if (islower(UChar(s[0]))) {
6643 	s[0] = (char) toupper(UChar(s[0]));
6644 	set_field_buffer(fld, 0, s);
6645     }
6646     return TRUE;
6647 }
6648 
6649 static
CHAR_CHECK_CB(mi_char_check)6650 CHAR_CHECK_CB(mi_char_check)
6651 {
6652     return ((isalpha(ch) || ch == '.') ? TRUE : FALSE);
6653 }
6654 
6655 /*
6656  * Passwords should be at least 6 characters.
6657  */
6658 static
FIELDCHECK_CB(pw_field_check)6659 FIELDCHECK_CB(pw_field_check)
6660 {
6661     char *s = field_buffer(fld, 0);
6662     int n;
6663 
6664     for (n = 0; s[n] != '\0'; ++n) {
6665 	if (isspace(UChar(s[n]))) {
6666 	    if (n < 6)
6667 		return FALSE;
6668 	}
6669     }
6670     return TRUE;
6671 }
6672 
6673 static
CHAR_CHECK_CB(pw_char_check)6674 CHAR_CHECK_CB(pw_char_check)
6675 {
6676     return (isgraph(ch) ? TRUE : FALSE);
6677 }
6678 
6679 static int
form_test(bool recur GCC_UNUSED)6680 form_test(bool recur GCC_UNUSED)
6681 {
6682     FORM *form;
6683     FIELD *f[12], *secure;
6684     FIELDTYPE *fty_middle = new_fieldtype(mi_field_check, mi_char_check);
6685     FIELDTYPE *fty_passwd = new_fieldtype(pw_field_check, pw_char_check);
6686     int c;
6687     unsigned n = 0;
6688 
6689 #ifdef NCURSES_MOUSE_VERSION
6690     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
6691 #endif
6692 
6693     move(18, 0);
6694     addstr("Defined edit/traversal keys:   ^Q/ESC- exit form\n");
6695     addstr("^N   -- go to next field       ^P  -- go to previous field\n");
6696     addstr("Home -- go to first field      End -- go to last field\n");
6697     addstr("^L   -- go to field to left    ^R  -- go to field to right\n");
6698     addstr("^U   -- move upward to field   ^D  -- move downward to field\n");
6699     addstr("^W   -- go to next word        ^B  -- go to previous word\n");
6700     addstr("^S   -- go to start of field   ^E  -- go to end of field\n");
6701     addstr("^H   -- delete previous char   ^Y  -- delete line\n");
6702     addstr("^G   -- delete current word    ^C  -- clear to end of line\n");
6703     addstr("^K   -- clear to end of field  ^X  -- clear field\n");
6704     addstr("Arrow keys move within a field as you would expect. ^] toggles overlay mode.");
6705 
6706     MvAddStr(4, 57, "Forms Entry Test");
6707 
6708     refresh();
6709 
6710     /* describe the form */
6711     memset(f, 0, sizeof(f));
6712     f[n++] = make_label(0, 15, "Sample Form");
6713 
6714     f[n++] = make_label(2, 0, "Last Name");
6715     f[n++] = make_field(3, 0, 1, 18, FALSE);
6716     set_field_type(f[n - 1], TYPE_ALPHA, 1);
6717 
6718     f[n++] = make_label(2, 20, "First Name");
6719     f[n++] = make_field(3, 20, 1, 12, FALSE);
6720     set_field_type(f[n - 1], TYPE_ALPHA, 1);
6721 
6722     f[n++] = make_label(2, 34, "Middle Name");
6723     f[n++] = make_field(3, 34, 1, 12, FALSE);
6724     set_field_type(f[n - 1], fty_middle);
6725 
6726     f[n++] = make_label(5, 0, "Comments");
6727     f[n++] = make_field(6, 0, 4, 46, FALSE);
6728 
6729     f[n++] = make_label(5, 20, "Password:");
6730     secure =
6731 	f[n++] = make_field(5, 30, 1, 9, TRUE);
6732     set_field_type(f[n - 1], fty_passwd);
6733     f[n] = (FIELD *) 0;
6734 
6735     if ((form = new_form(f)) != 0) {
6736 	WINDOW *w;
6737 	int finished = 0;
6738 
6739 	display_form(form);
6740 
6741 	w = form_win(form);
6742 	raw();
6743 	nonl();			/* lets us read ^M's */
6744 	while (!finished) {
6745 	    switch (form_driver(form, c = form_virtualize(form, w))) {
6746 	    case E_OK:
6747 		MvAddStr(5, 57, field_buffer(secure, 1));
6748 		clrtoeol();
6749 		refresh();
6750 		break;
6751 	    case E_UNKNOWN_COMMAND:
6752 		finished = my_form_driver(form, c);
6753 		break;
6754 	    default:
6755 		beep();
6756 		break;
6757 	    }
6758 	}
6759 
6760 	erase_form(form);
6761 
6762 	free_form(form);
6763     }
6764     for (c = 0; f[c] != 0; c++)
6765 	free_field(f[c]);
6766     free_fieldtype(fty_middle);
6767     free_fieldtype(fty_passwd);
6768     noraw();
6769     nl();
6770 
6771 #ifdef NCURSES_MOUSE_VERSION
6772     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
6773 #endif
6774     return OK;
6775 }
6776 #endif /* USE_LIBFORM */
6777 
6778 /****************************************************************************
6779  *
6780  * Overlap test
6781  *
6782  ****************************************************************************/
6783 
6784 #if HAVE_COPYWIN		/* ...and overlay, overwrite */
6785 
6786 static const int overlap_HEAD = 1;
6787 static const int overlap_FOOT = 6;
6788 
6789 static WINDOW *
make_overlap(int n)6790 make_overlap(int n)
6791 {
6792     WINDOW *result;
6793     int y, x;
6794 
6795     getmaxyx(stdscr, y, x);
6796     if (y < 23 || x < 80) {
6797 	Cannot("The screen is too small for this test");
6798 	result = 0;
6799     } else {
6800 	int ymax = y - (overlap_HEAD + overlap_FOOT);
6801 	int high = ymax / 5;	/* equal-sized parts for cross */
6802 	int xmax = x - 2;	/* margin */
6803 	int wide = (xmax / 5) & ~1;
6804 	int lmar, tmar;
6805 
6806 	if (high > 8)
6807 	    high = 8;
6808 
6809 	if (wide > 8)
6810 	    wide = 8;
6811 
6812 	tmar = (ymax - (5 * high)) / 2 + overlap_HEAD;
6813 	lmar = (xmax - (5 * wide)) / 2;
6814 
6815 	if (n == 0) {
6816 	    result = newwin(3 * high, 3 * wide, tmar, lmar);
6817 	} else {
6818 	    result = newwin(3 * high, 3 * wide, tmar + 2 * high, lmar + 2 * wide);
6819 	}
6820     }
6821     return result;
6822 }
6823 
6824 static void
clear_overlap(void)6825 clear_overlap(void)
6826 {
6827     int row;
6828 
6829     for (row = overlap_HEAD; row < LINES - overlap_FOOT; ++row) {
6830 	move(row, 0);
6831 	clrtoeol();
6832     }
6833 }
6834 
6835 static int
move_overlap(int shift,WINDOW * win1)6836 move_overlap(int shift, WINDOW *win1)
6837 {
6838     int ymax = getmaxy(stdscr) - (overlap_HEAD + overlap_FOOT);
6839     int high = ymax / 5;	/* equal-sized parts for cross */
6840     int tmar;
6841     int xmax1 = getmaxx(win1) + 1;
6842     int lmar1 = (COLS - (5 * (xmax1) / 3)) / 2;
6843     int rc = ERR;
6844 
6845     if (high > 8)
6846 	high = 8;
6847     tmar = (ymax - (5 * high)) / 2 + overlap_HEAD;
6848 
6849     rc = mvwin(win1, tmar, lmar1 + shift);
6850     return rc;
6851 }
6852 
6853 static void
fillwin(WINDOW * win,char ch)6854 fillwin(WINDOW *win, char ch)
6855 {
6856     int y, x;
6857     int y1, x1;
6858 
6859     getmaxyx(win, y1, x1);
6860     for (y = 0; y < y1; y++) {
6861 	wmove(win, y, 0);
6862 	for (x = 0; x < x1; x++)
6863 	    waddch(win, UChar(ch));
6864     }
6865 }
6866 
6867 #define InCross(x,y, x1,y1) \
6868 	    (((x > (x1 - 1) / 3) && (x <= (2 * (x1 - 1)) / 3)) \
6869 		|| (((y > (y1 - 1) / 3) && (y <= (2 * (y1 - 1)) / 3))))
6870 
6871 static void
crosswin(WINDOW * win,char ch)6872 crosswin(WINDOW *win, char ch)
6873 {
6874     int y, x;
6875     int y1, x1;
6876     int xw = 1;
6877 
6878     getmaxyx(win, y1, x1);
6879     for (y = 0; y < y1; y++) {
6880 	for (x = 0; x < x1; x += xw) {
6881 	    if (InCross(x, y, x1, y1)) {
6882 		wmove(win, y, x);
6883 		waddch(win, UChar(ch));
6884 	    }
6885 	}
6886     }
6887 }
6888 
6889 /*
6890  * Match "crosswin()", but using line-drawing characters.  This could be done
6891  * a little simpler using box(), but the reason for this example is to test
6892  * hline/vline and addch with line-drawing vs the copy/overlay functions.
6893  */
6894 static void
crossbox(WINDOW * win)6895 crossbox(WINDOW *win)
6896 {
6897     int y1, x1;
6898     int ymax, xmax;
6899 
6900     getmaxyx(win, y1, x1);
6901 
6902     ymax = (y1 + 1);
6903     xmax = (x1 + 1);
6904 
6905     mvwhline(win, 0, (xmax / 3), ACS_HLINE, (xmax / 3));
6906     mvwhline(win, ymax / 3, 0, ACS_HLINE, xmax);
6907     mvwhline(win, ((2 * ymax) / 3) - 1, 0, ACS_HLINE, xmax);
6908     mvwhline(win, y1 - 1, (xmax / 3), ACS_HLINE, (xmax / 3));
6909 
6910     mvwvline(win, (ymax / 3), 0, ACS_VLINE, (ymax / 3));
6911     mvwvline(win, 0, xmax / 3, ACS_VLINE, ymax);
6912     mvwvline(win, 0, ((2 * xmax) / 3) - 1, ACS_VLINE, ymax);
6913     mvwvline(win, (ymax / 3), x1 - 1, ACS_VLINE, (ymax / 3));
6914 
6915     mvwaddch(win, 0, (xmax / 3), ACS_ULCORNER);
6916     mvwaddch(win, 0, ((2 * xmax) / 3) - 1, ACS_URCORNER);
6917     mvwaddch(win, y1 - 1, (xmax / 3), ACS_LLCORNER);
6918     mvwaddch(win, y1 - 1, ((2 * xmax) / 3) - 1, ACS_LRCORNER);
6919 
6920     mvwaddch(win, (ymax / 3), 0, ACS_ULCORNER);
6921     mvwaddch(win, ((2 * ymax) / 3) - 1, 0, ACS_LLCORNER);
6922     mvwaddch(win, (ymax / 3), x1 - 1, ACS_URCORNER);
6923     mvwaddch(win, ((2 * ymax) / 3) - 1, x1 - 1, ACS_LRCORNER);
6924 
6925     mvwaddch(win, (ymax / 3), (xmax / 3), ACS_PLUS);
6926     mvwaddch(win, (ymax / 3), ((2 * xmax) / 3) - 1, ACS_PLUS);
6927     mvwaddch(win, ((2 * ymax) / 3) - 1, ((2 * xmax) / 3) - 1, ACS_PLUS);
6928     mvwaddch(win, ((2 * ymax) / 3) - 1, (xmax / 3), ACS_PLUS);
6929 }
6930 
6931 typedef enum {
6932     otBASE_refresh = 0
6933     ,otBASE_fill
6934     ,otBASE_draw
6935     ,otBASE_clear
6936     ,otBASE_copy
6937 } otBASE;
6938 
6939 #define OVERLAP_FLAVORS 6
6940 
6941 typedef enum {
6942     otFILL_normal = 0
6943     ,otFILL_bold
6944     ,otFILL_color
6945     ,otFILL_bright
6946 } otFILL;
6947 
6948 #define LimitFILL() UseColors ? 4 : 2
6949 
6950 typedef enum {
6951     otDRAW_text_cross = 0
6952     ,otDRAW_line_box
6953     ,otDRAW_line_cross
6954     ,otDRAW_set_bg
6955     ,otDRAW_reset_bg
6956 } otDRAW;
6957 
6958 #define LimitDRAW() UseColors ? 5 : 3
6959 
6960 typedef enum {
6961     otCOPY_overwrite = 0
6962     ,otCOPY_merge
6963     ,otCOPY_force
6964     ,otCOPY_overlay
6965 } otCOPY;
6966 
6967 #define LimitCOPY() 4
6968 
6969 static void
overlap_helpitem(int state,int item,char * message)6970 overlap_helpitem(int state, int item, char *message)
6971 {
6972     int row = (item / 2);
6973     int col = ((item % 2) ? COLS / 2 : 0);
6974 
6975     move(LINES - 6 + row, col);
6976     printw("%c%c = %s", state == row ? '>' : ' ', 'a' + item, message);
6977     clrtoeol();
6978 }
6979 
6980 static void
overlap_test_1_attr(WINDOW * win,int flavor,int col)6981 overlap_test_1_attr(WINDOW *win, int flavor, int col)
6982 {
6983     NCURSES_PAIRS_T cpair = (NCURSES_PAIRS_T) (1 + (flavor * 2) + col);
6984 
6985     switch ((otFILL) flavor) {
6986     case otFILL_normal:
6987 	(void) wattrset(win, A_NORMAL);
6988 	break;
6989     case otFILL_bold:
6990 	(void) wattrset(win, A_BOLD);
6991 	break;
6992     case otFILL_color:
6993 	init_pair(cpair, COLOR_BLUE, COLOR_WHITE);
6994 	(void) wattrset(win, AttrArg(COLOR_PAIR(cpair), A_NORMAL));
6995 	break;
6996     case otFILL_bright:
6997 	init_pair(cpair, COLOR_WHITE, COLOR_BLUE);
6998 	(void) wattrset(win, AttrArg(COLOR_PAIR(cpair), A_BOLD));
6999 	break;
7000     }
7001 }
7002 
7003 static void
overlap_test_2_attr(WINDOW * win,int flavor,int col)7004 overlap_test_2_attr(WINDOW *win, int flavor, int col)
7005 {
7006     NCURSES_PAIRS_T cpair = (NCURSES_PAIRS_T) (9 + (flavor * 2) + col);
7007 
7008     switch ((otDRAW) flavor) {
7009     case otDRAW_text_cross:
7010 	/* no effect */
7011 	break;
7012     case otDRAW_line_box:
7013 	/* no effect */
7014 	break;
7015     case otDRAW_line_cross:
7016 	/* no effect */
7017 	break;
7018     case otDRAW_set_bg:
7019 	init_pair(cpair, COLOR_RED, COLOR_GREEN);
7020 	wbkgdset(win, colored_chtype(' ', A_BLINK, cpair));
7021 	break;
7022     case otDRAW_reset_bg:
7023 	wbkgdset(win, ' ' | A_NORMAL);
7024 	break;
7025     }
7026 }
7027 
7028 static int
overlap_help(int state,int flavors[OVERLAP_FLAVORS])7029 overlap_help(int state, int flavors[OVERLAP_FLAVORS])
7030 {
7031     int item;
7032     int limit[OVERLAP_FLAVORS];
7033     char msg[80];
7034 
7035     if (state < 0)
7036 	state += OVERLAP_FLAVORS;
7037     state = state % OVERLAP_FLAVORS;
7038     assert(state >= 0 && state < OVERLAP_FLAVORS);
7039 
7040     for (item = 0; item < (2 * OVERLAP_FLAVORS); ++item) {
7041 	int row = item / 2;
7042 	int col = item % 2;
7043 	const char *ths = col ? "B" : "A";
7044 	const char *tht = col ? "A" : "B";
7045 
7046 	switch ((otBASE) row) {
7047 	case otBASE_refresh:
7048 	    limit[row] = 1;
7049 	    flavors[row] = 0;
7050 	    _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7051 			"refresh %s, then %s, then doupdate.", ths, tht);
7052 	    break;
7053 	case otBASE_fill:
7054 	    limit[row] = LimitFILL();
7055 	    flavors[row] %= limit[row];
7056 	    overlap_test_1_attr(stdscr, flavors[row], col);
7057 	    _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7058 			"fill window %s with letter %s.", ths, ths);
7059 	    break;
7060 	case otBASE_draw:
7061 	    limit[row] = LimitDRAW();
7062 	    flavors[row] %= limit[row];
7063 	    switch ((otDRAW) flavors[row]) {
7064 	    case otDRAW_text_cross:
7065 		_nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7066 			    "cross text-pattern in window %s.", ths);
7067 		break;
7068 	    case otDRAW_line_box:
7069 		_nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7070 			    "draw line-box in window %s.", ths);
7071 		break;
7072 	    case otDRAW_line_cross:
7073 		_nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7074 			    "draw line-cross in window %s.", ths);
7075 		break;
7076 	    case otDRAW_set_bg:
7077 		_nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7078 			    "set background of window %s.", ths);
7079 		break;
7080 	    case otDRAW_reset_bg:
7081 		_nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7082 			    "reset background of window %s.", ths);
7083 		break;
7084 	    }
7085 	    break;
7086 	case otBASE_clear:
7087 	    limit[row] = 1;
7088 	    flavors[row] = 0;
7089 	    _nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7090 			"clear window %s.", ths);
7091 	    break;
7092 	case otBASE_copy:
7093 	    limit[row] = LimitCOPY();
7094 	    flavors[row] %= limit[row];
7095 	    switch ((otCOPY) flavors[row]) {
7096 	    case otCOPY_overwrite:
7097 		_nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7098 			    "overwrite %s onto %s.", ths, tht);
7099 		break;
7100 	    case otCOPY_merge:
7101 		_nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7102 			    "copywin(FALSE) %s onto %s.", ths, tht);
7103 		break;
7104 	    case otCOPY_force:
7105 		_nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7106 			    "copywin(TRUE) %s onto %s.", ths, tht);
7107 		break;
7108 	    case otCOPY_overlay:
7109 		_nc_SPRINTF(msg, _nc_SLIMIT(sizeof(msg))
7110 			    "overlay %s onto %s.", ths, tht);
7111 		break;
7112 	    }
7113 	    break;
7114 	}
7115 	overlap_helpitem(state, item, msg);
7116 	(void) wattrset(stdscr, A_NORMAL);
7117 	wbkgdset(stdscr, ' ' | A_NORMAL);
7118     }
7119     move(LINES - 1, 0);
7120     printw("^Q/ESC = terminate test. </> shift. Up/down/space select (row %d",
7121 	   state + 1);
7122     if (limit[state] > 1)
7123 	printw(" test %d:%d", 1 + flavors[state], limit[state]);
7124     printw(").");
7125     clrtoeol();
7126 
7127     return state;
7128 }
7129 
7130 static void
overlap_test_0(WINDOW * a,WINDOW * b)7131 overlap_test_0(WINDOW *a, WINDOW *b)
7132 {
7133     touchwin(a);
7134     touchwin(b);
7135     wnoutrefresh(a);
7136     wnoutrefresh(b);
7137     doupdate();
7138 }
7139 
7140 static void
overlap_test_1(int flavor,int col,WINDOW * a,char fill)7141 overlap_test_1(int flavor, int col, WINDOW *a, char fill)
7142 {
7143     overlap_test_1_attr(a, flavor, col);
7144     fillwin(a, fill);
7145     (void) wattrset(a, A_NORMAL);
7146 }
7147 
7148 static void
overlap_test_2(int flavor,int col,WINDOW * a,char fill)7149 overlap_test_2(int flavor, int col, WINDOW *a, char fill)
7150 {
7151     overlap_test_2_attr(a, flavor, col);
7152     switch ((otDRAW) flavor) {
7153     case otDRAW_text_cross:
7154 	crosswin(a, fill);
7155 	break;
7156     case otDRAW_line_box:
7157 	box(a, 0, 0);
7158 	break;
7159     case otDRAW_line_cross:
7160 	crossbox(a);
7161 	break;
7162     case otDRAW_set_bg:
7163 	/* done in overlap_test_2_attr */
7164 	break;
7165     case otDRAW_reset_bg:
7166 	/* done in overlap_test_2_attr */
7167 	break;
7168     }
7169 }
7170 
7171 static void
overlap_test_3(WINDOW * a)7172 overlap_test_3(WINDOW *a)
7173 {
7174     wclear(a);
7175     wmove(a, 0, 0);
7176 }
7177 
7178 static void
overlap_test_4(int flavor,WINDOW * a,WINDOW * b)7179 overlap_test_4(int flavor, WINDOW *a, WINDOW *b)
7180 {
7181     switch ((otCOPY) flavor) {
7182     case otCOPY_overwrite:
7183 	overwrite(a, b);
7184 	break;
7185     case otCOPY_merge:
7186 	copywin(a, b, 0, 0, 0, 0, getmaxy(b), getmaxx(b), FALSE);
7187 	break;
7188     case otCOPY_force:
7189 	copywin(a, b, 0, 0, 0, 0, getmaxy(b), getmaxx(b), TRUE);
7190 	break;
7191     case otCOPY_overlay:
7192 	overlay(a, b);
7193 	break;
7194     }
7195 }
7196 
7197 /* test effects of overlapping windows */
7198 static int
overlap_test(bool recur GCC_UNUSED)7199 overlap_test(bool recur GCC_UNUSED)
7200 {
7201     WINDOW *win1, *win2;
7202     int ch;
7203     int shift = 0, last_refresh = -1;
7204     int state, flavor[OVERLAP_FLAVORS];
7205 
7206     if ((win1 = make_overlap(0)) == 0) {
7207 	return ERR;
7208     } else if ((win2 = make_overlap(1)) == 0) {
7209 	delwin(win1);
7210 	return ERR;
7211     }
7212 
7213     curs_set(0);
7214     raw();
7215     refresh();
7216     move(0, 0);
7217     printw("Test wnoutrefresh() for two overlapping windows:");
7218 
7219     memset(flavor, 0, sizeof(flavor));
7220     state = overlap_help(0, flavor);
7221 
7222     while (!isQuit(ch = Getchar(), TRUE)) {
7223 	switch (ch) {
7224 	case 'a':		/* refresh window A first, then B */
7225 	    overlap_test_0(win1, win2);
7226 	    break;
7227 
7228 	case 'b':		/* refresh window B first, then A */
7229 	    overlap_test_0(win2, win1);
7230 	    break;
7231 
7232 	case 'c':		/* fill window A so it is visible */
7233 	    overlap_test_1(flavor[otBASE_fill], 0, win1, 'A');
7234 	    break;
7235 
7236 	case 'd':		/* fill window B so it is visible */
7237 	    overlap_test_1(flavor[otBASE_fill], 1, win2, 'B');
7238 	    break;
7239 
7240 	case 'e':		/* cross test pattern in window A */
7241 	    overlap_test_2(flavor[otBASE_draw], 0, win1, 'A');
7242 	    break;
7243 
7244 	case 'f':		/* cross test pattern in window A */
7245 	    overlap_test_2(flavor[otBASE_draw], 1, win2, 'B');
7246 	    break;
7247 
7248 	case 'g':		/* clear window A */
7249 	    overlap_test_3(win1);
7250 	    break;
7251 
7252 	case 'h':		/* clear window B */
7253 	    overlap_test_3(win2);
7254 	    break;
7255 
7256 	case 'i':		/* overwrite A onto B */
7257 	    overlap_test_4(flavor[otBASE_copy], win1, win2);
7258 	    break;
7259 
7260 	case 'j':		/* overwrite B onto A */
7261 	    overlap_test_4(flavor[otBASE_copy], win2, win1);
7262 	    break;
7263 
7264 	case CTRL('n'):
7265 	case KEY_DOWN:
7266 	    state = overlap_help(state + 1, flavor);
7267 	    break;
7268 
7269 	case CTRL('p'):
7270 	case KEY_UP:
7271 	    state = overlap_help(state - 1, flavor);
7272 	    break;
7273 
7274 	case ' ':
7275 	    flavor[state] += 1;
7276 	    state = overlap_help(state, flavor);
7277 	    break;
7278 
7279 	case HELP_KEY_1:
7280 	    state = overlap_help(state, flavor);
7281 	    break;
7282 
7283 	case '<':
7284 	    /* FALLTHRU */
7285 	case '>':
7286 	    /* see below */
7287 	    break;
7288 
7289 	default:
7290 	    beep();
7291 	    break;
7292 	}
7293 
7294 	switch (ch) {
7295 	case 'a':
7296 	    /* FALLTHRU */
7297 	case 'b':
7298 	    last_refresh = ch;
7299 	    break;
7300 	case '<':
7301 	    shift -= 2;
7302 	    /* FALLTHRU */
7303 	case '>':
7304 	    shift += 1;
7305 	    if (move_overlap(shift, win1) != OK) {
7306 		flash();
7307 		shift += (ch == '>') ? -1 : 1;
7308 	    } else if (last_refresh > 0) {
7309 		clear_overlap();
7310 		wnoutrefresh(stdscr);
7311 		if (last_refresh == 'a')
7312 		    overlap_test_0(win1, win2);
7313 		else
7314 		    overlap_test_0(win2, win1);
7315 	    }
7316 	    break;
7317 	default:
7318 	    last_refresh = -1;
7319 	    break;
7320 	}
7321     }
7322 
7323     delwin(win2);
7324     delwin(win1);
7325     erase();
7326     stop_curses();
7327     return OK;
7328 }
7329 
7330 #if USE_WIDEC_SUPPORT
7331 static void
x_fillwin(WINDOW * win,wchar_t ch)7332 x_fillwin(WINDOW *win, wchar_t ch)
7333 {
7334     int y, x;
7335     int y1, x1;
7336 
7337     getmaxyx(win, y1, x1);
7338     x1 /= 2;
7339     for (y = 0; y < y1; y++) {
7340 	wmove(win, y, 0);
7341 	for (x = 0; x < x1; x++)
7342 	    waddnwstr(win, &ch, 1);
7343     }
7344 }
7345 
7346 static void
x_crosswin(WINDOW * win,wchar_t ch)7347 x_crosswin(WINDOW *win, wchar_t ch)
7348 {
7349     int y, x;
7350     int y1, x1;
7351     int xw = 2;
7352 
7353     getmaxyx(win, y1, x1);
7354     for (y = 0; y < y1; y++) {
7355 	for (x = 0; x < x1; x += xw) {
7356 	    if (InCross(x, y, x1, y1)) {
7357 		wmove(win, y, x);
7358 		waddnwstr(win, &ch, 1);
7359 	    }
7360 	}
7361     }
7362 }
7363 
7364 static void
x_overlap_test_1(int flavor,int col,WINDOW * a,wchar_t fill)7365 x_overlap_test_1(int flavor, int col, WINDOW *a, wchar_t fill)
7366 {
7367     overlap_test_1_attr(a, flavor, col);
7368     x_fillwin(a, fill);
7369     (void) wattrset(a, A_NORMAL);
7370 }
7371 
7372 static void
x_overlap_test_2(int flavor,int col,WINDOW * a,wchar_t fill)7373 x_overlap_test_2(int flavor, int col, WINDOW *a, wchar_t fill)
7374 {
7375     overlap_test_2_attr(a, flavor, col);
7376     switch ((otDRAW) flavor) {
7377     case otDRAW_text_cross:
7378 	x_crosswin(a, fill);
7379 	break;
7380     case otDRAW_line_box:
7381 	box(a, 0, 0);
7382 	break;
7383     case otDRAW_line_cross:
7384 	crossbox(a);
7385 	break;
7386     case otDRAW_set_bg:
7387 	/* done in overlap_test_2_attr */
7388 	break;
7389     case otDRAW_reset_bg:
7390 	/* done in overlap_test_2_attr */
7391 	break;
7392     }
7393 }
7394 
7395 /* test effects of overlapping windows */
7396 static int
x_overlap_test(bool recur GCC_UNUSED)7397 x_overlap_test(bool recur GCC_UNUSED)
7398 {
7399     const wchar_t WIDE_A = 0xff21;
7400     const wchar_t WIDE_B = 0xff22;
7401     WINDOW *win1, *win2;
7402     int ch;
7403     int shift = 0, last_refresh = -1;
7404     int state, flavor[OVERLAP_FLAVORS];
7405 
7406     if ((win1 = make_overlap(0)) == 0) {
7407 	return ERR;
7408     } else if ((win2 = make_overlap(1)) == 0) {
7409 	delwin(win1);
7410 	return ERR;
7411     }
7412 
7413     curs_set(0);
7414     raw();
7415     refresh();
7416     move(0, 0);
7417     printw("Test wnoutrefresh() for overlapping windows with double-cell characters:");
7418 
7419     memset(flavor, 0, sizeof(flavor));
7420     state = overlap_help(0, flavor);
7421 
7422     while (!isQuit(ch = Getchar(), TRUE)) {
7423 	switch (ch) {
7424 	case 'a':		/* refresh window A first, then B */
7425 	    overlap_test_0(win1, win2);
7426 	    break;
7427 
7428 	case 'b':		/* refresh window B first, then A */
7429 	    overlap_test_0(win2, win1);
7430 	    break;
7431 
7432 	case 'c':		/* fill window A so it is visible */
7433 	    x_overlap_test_1(flavor[otBASE_fill], 0, win1, WIDE_A);
7434 	    break;
7435 
7436 	case 'd':		/* fill window B so it is visible */
7437 	    x_overlap_test_1(flavor[otBASE_fill], 1, win2, WIDE_B);
7438 	    break;
7439 
7440 	case 'e':		/* cross test pattern in window A */
7441 	    x_overlap_test_2(flavor[otBASE_draw], 0, win1, WIDE_A);
7442 	    break;
7443 
7444 	case 'f':		/* cross test pattern in window A */
7445 	    x_overlap_test_2(flavor[otBASE_draw], 1, win2, WIDE_B);
7446 	    break;
7447 
7448 	case 'g':		/* clear window A */
7449 	    overlap_test_3(win1);
7450 	    break;
7451 
7452 	case 'h':		/* clear window B */
7453 	    overlap_test_3(win2);
7454 	    break;
7455 
7456 	case 'i':		/* overwrite A onto B */
7457 	    overlap_test_4(flavor[otBASE_copy], win1, win2);
7458 	    break;
7459 
7460 	case 'j':		/* overwrite B onto A */
7461 	    overlap_test_4(flavor[otBASE_copy], win2, win1);
7462 	    break;
7463 
7464 	case CTRL('n'):
7465 	case KEY_DOWN:
7466 	    state = overlap_help(state + 1, flavor);
7467 	    break;
7468 
7469 	case CTRL('p'):
7470 	case KEY_UP:
7471 	    state = overlap_help(state - 1, flavor);
7472 	    break;
7473 
7474 	case ' ':
7475 	    flavor[state] += 1;
7476 	    state = overlap_help(state, flavor);
7477 	    break;
7478 
7479 	case HELP_KEY_1:
7480 	    state = overlap_help(state, flavor);
7481 	    break;
7482 
7483 	case '<':
7484 	    /* FALLTHRU */
7485 	case '>':
7486 	    /* see below */
7487 	    break;
7488 
7489 	default:
7490 	    beep();
7491 	    break;
7492 	}
7493 
7494 	switch (ch) {
7495 	case 'a':
7496 	    /* FALLTHRU */
7497 	case 'b':
7498 	    last_refresh = ch;
7499 	    break;
7500 	case '<':
7501 	    shift -= 2;
7502 	    /* FALLTHRU */
7503 	case '>':
7504 	    shift += 1;
7505 	    if (move_overlap(shift, win1) != OK) {
7506 		flash();
7507 		shift += (ch == '>') ? -1 : 1;
7508 	    } else if (last_refresh > 0) {
7509 		clear_overlap();
7510 		wnoutrefresh(stdscr);
7511 		if (last_refresh == 'a')
7512 		    overlap_test_0(win1, win2);
7513 		else
7514 		    overlap_test_0(win2, win1);
7515 	    }
7516 	    break;
7517 	default:
7518 	    last_refresh = -1;
7519 	    break;
7520 	}
7521     }
7522 
7523     delwin(win2);
7524     delwin(win1);
7525     erase();
7526     stop_curses();
7527     return OK;
7528 }
7529 #endif /* USE_WIDEC_SUPPORT */
7530 
7531 #endif /* HAVE_COPYWIN */
7532 
7533 static void
show_setting_name(const char * name)7534 show_setting_name(const char *name)
7535 {
7536     printw("%-25s ", name);
7537 }
7538 
7539 static void
show_string_setting(const char * name,const char * value)7540 show_string_setting(const char *name, const char *value)
7541 {
7542     show_setting_name(name);
7543     if (value) {
7544 	printw("\"%s\"", value);
7545     } else {
7546 	attron(A_REVERSE);
7547 	addstr("<NULL>");
7548 	attroff(A_REVERSE);
7549     }
7550     AddCh('\n');
7551 }
7552 
7553 static void
show_number_setting(const char * name,int value)7554 show_number_setting(const char *name, int value)
7555 {
7556     show_setting_name(name);
7557     if (value >= 0) {
7558 	printw("%d", value);
7559     } else {
7560 	attron(A_REVERSE);
7561 	printw("%d", value);
7562 	attroff(A_REVERSE);
7563     }
7564     AddCh('\n');
7565 }
7566 
7567 static void
show_boolean_setting(const char * name,int value)7568 show_boolean_setting(const char *name, int value)
7569 {
7570     show_setting_name(name);
7571     if (value >= 0) {
7572 	printw("%s", value ? "TRUE" : "FALSE");
7573     } else {
7574 	attron(A_REVERSE);
7575 	printw("%d", value);
7576 	attroff(A_REVERSE);
7577     }
7578     AddCh('\n');
7579 }
7580 
7581 static int
settings_test(bool recur GCC_UNUSED)7582 settings_test(bool recur GCC_UNUSED)
7583 {
7584 #if USE_WIDEC_SUPPORT
7585     wchar_t ch;
7586 #endif
7587 
7588     move(0, 0);
7589     show_string_setting("termname", termname());
7590     show_string_setting("longname", longname());
7591     show_number_setting("baudrate", baudrate());
7592     if (erasechar() > 0) {
7593 	show_string_setting("unctrl(erasechar)", unctrl((chtype) erasechar()));
7594 	show_string_setting("keyname(erasechar)", keyname(erasechar()));
7595     }
7596     if (killchar() > 0) {
7597 	show_string_setting("unctrl(killchar)", unctrl((chtype) killchar()));
7598 	show_string_setting("keyname(killchar)", keyname(killchar()));
7599     }
7600 #if USE_WIDEC_SUPPORT
7601     if (erasewchar(&ch) == OK) {
7602 	show_string_setting("key_name(erasewchar)", key_name(ch));
7603     }
7604     if (killwchar(&ch) == OK) {
7605 	show_string_setting("key_name(killwchar)", key_name(ch));
7606     }
7607 #endif
7608     show_boolean_setting("has_ic", has_ic());
7609     show_boolean_setting("has_il", has_il());
7610     show_boolean_setting("has_colors", has_colors());
7611 #if HAVE_COLOR_CONTENT
7612     show_boolean_setting("can_change_color", can_change_color());
7613 #endif
7614     show_setting_name("LINES");
7615     printw("%d\n", LINES);
7616     show_setting_name("COLS");
7617     printw("%d\n", COLS);
7618     Pause();
7619     erase();
7620     stop_curses();
7621     return OK;
7622 }
7623 
7624 /****************************************************************************
7625  *
7626  * Main sequence
7627  *
7628  ****************************************************************************/
7629 
7630 static void
usage(int ok)7631 usage(int ok)
7632 {
7633     static const char *const tbl[] =
7634     {
7635 	"Usage: ncurses [options]"
7636 	,""
7637 	,USAGE_COMMON
7638 	,"Options:"
7639 #ifdef NCURSES_VERSION
7640 	," -a f,b   set default-colors (assumed white-on-black)"
7641 	," -d       use default-colors if terminal supports them"
7642 #endif
7643 #if HAVE_USE_ENV
7644 	," -E       call use_env(FALSE) to ignore $LINES and $COLUMNS"
7645 #endif
7646 #if USE_SOFTKEYS
7647 	," -e fmt   specify format for soft-keys test (e)"
7648 #endif
7649 #if HAVE_RIPOFFLINE
7650 	," -F       rip-off footer line (can repeat)"
7651 	," -H       rip-off header line (can repeat)"
7652 #endif
7653 	," -m       do not use colors"
7654 #if HAVE_COLOR_CONTENT
7655 	," -p file  rgb values to use in 'd' rather than ncurses's builtin"
7656 #endif
7657 #if USE_LIBPANEL
7658 	," -s msec  specify nominal time for panel-demo (default: 1, to hold)"
7659 #endif
7660 #if defined(NCURSES_VERSION_PATCH) && (NCURSES_VERSION_PATCH >= 20120714) && !defined(_NC_WINDOWS)
7661 	," -T       call use_tioctl(TRUE) to allow SIGWINCH to override environment"
7662 #endif
7663 #ifdef TRACE
7664 	," -t mask  specify default trace-level (may toggle with ^T)"
7665 #endif
7666 #if HAVE_COLOR_CONTENT
7667 	," -x       use xterm-compatible control for reading color palette"
7668 #endif
7669     };
7670     size_t n;
7671     for (n = 0; n < SIZEOF(tbl); n++)
7672 	fprintf(stderr, "%s\n", tbl[n]);
7673     ExitProgram(ok ? EXIT_SUCCESS : EXIT_FAILURE);
7674 }
7675 
7676 static void
set_terminal_modes(void)7677 set_terminal_modes(void)
7678 {
7679     noraw();
7680     cbreak();
7681     noecho();
7682     scrollok(stdscr, TRUE);
7683     idlok(stdscr, TRUE);
7684     keypad(stdscr, TRUE);
7685 }
7686 
7687 #ifdef SIGUSR1
7688 static void
announce_sig(int sig)7689 announce_sig(int sig)
7690 {
7691     (void) fprintf(stderr, "Handled signal %d\r\n", sig);
7692 }
7693 #endif
7694 
7695 #if HAVE_RIPOFFLINE
7696 static int
rip_footer(WINDOW * win,int cols)7697 rip_footer(WINDOW *win, int cols)
7698 {
7699     wbkgd(win, A_REVERSE);
7700     werase(win);
7701     wmove(win, 0, 0);
7702     wprintw(win, "footer: window %p, %d columns", (void *) win, cols);
7703     wnoutrefresh(win);
7704     return OK;
7705 }
7706 
7707 static int
rip_header(WINDOW * win,int cols)7708 rip_header(WINDOW *win, int cols)
7709 {
7710     wbkgd(win, A_REVERSE);
7711     werase(win);
7712     wmove(win, 0, 0);
7713     wprintw(win, "header: window %p, %d columns", (void *) win, cols);
7714     wnoutrefresh(win);
7715     return OK;
7716 }
7717 #endif /* HAVE_RIPOFFLINE */
7718 
7719 static void
main_menu(bool top)7720 main_menu(bool top)
7721 {
7722 #if USE_WIDEC_SUPPORT
7723     typedef struct {
7724 	bool recur;
7725 	int (*narrow_func) (bool);
7726 	int (*wide_func) (bool);
7727 	int code;
7728 	const char *help;
7729     } MyCmds;
7730 #define BOTH(a)   a, x_ ## a
7731 #define ONLY(a)   a, NULL
7732 #define CMDS(recur, funcs,code,help) { recur, funcs, code, help }
7733 #else
7734     typedef struct {
7735 	bool recur;
7736 	int (*narrow_func) (bool);
7737 	int code;
7738 	const char *help;
7739     } MyCmds;
7740 #define BOTH(a)   a
7741 #define ONLY(a)   a
7742 #define CMDS(recur, funcs,code,help) { recur, funcs, code, help }
7743 #endif
7744     /* *INDENT-OFF* */
7745     static MyCmds cmds[] =
7746     {
7747 	CMDS(TRUE, BOTH(getch_test),	'a', "keyboard and mouse input test"),
7748 	CMDS(TRUE, BOTH(attr_test),	'b', "character attribute test"),
7749 	CMDS(TRUE, BOTH(color_test),	'c', "color test pattern"),
7750 #if HAVE_COLOR_CONTENT
7751 	CMDS(FALSE, ONLY(color_edit),	'd', "edit RGB color values"),
7752 #endif
7753 #if USE_SOFTKEYS
7754 	CMDS(TRUE, BOTH(slk_test),	'e', "exercise soft keys"),
7755 #endif
7756 	CMDS(TRUE, BOTH(acs_test),	'f', "display ACS characters"),
7757 	CMDS(TRUE, ONLY(scroll_test),   'g', "display windows and scrolling"),
7758 	CMDS(TRUE, ONLY(flushinp_test),	'i', "test flushinp()"),
7759 	CMDS(TRUE, ONLY(sgr_attr_test),	'k', "display character attributes"),
7760 #if USE_LIBMENU
7761 	CMDS(TRUE, ONLY(menu_test),	'm', "exercise menu library"),
7762 #endif
7763 #if USE_LIBPANEL
7764 	CMDS(TRUE, BOTH(panel_test),	'o', "exercise panel library"),
7765 #endif
7766 #if HAVE_NEWPAD
7767 	CMDS(TRUE, ONLY(pad_test),	'p', "exercise pad features"),
7768 #endif
7769 	CMDS(TRUE, ONLY(NULL),		'q', "quit"),
7770 #if USE_LIBMENU
7771 	CMDS(TRUE, ONLY(form_test),	'r', "exercise form library"),
7772 #endif
7773 #if HAVE_COPYWIN
7774 	CMDS(TRUE, BOTH(overlap_test),	's', "overlapping-refresh test"),
7775 #endif
7776 #if USE_LIBMENU && defined(TRACE)
7777 	CMDS(TRUE, ONLY(trace_set),	't', "set trace level"),
7778 #endif
7779 	CMDS(TRUE, ONLY(settings_test),	'v', "show terminal name and settings"),
7780 	CMDS(FALSE, ONLY(NULL),		'?', "repeat this command summary")
7781     };
7782     /* *INDENT-ON* */
7783 
7784     int (*doit) (bool);
7785     char command;
7786     unsigned n;
7787     do {
7788 	printf("This is the ncurses main menu (uppercase for wide-characters)\n");
7789 	for (n = 0; n < SIZEOF(cmds); ++n) {
7790 	    if (top || cmds[n].recur) {
7791 		putchar(' ');
7792 #if USE_WIDEC_SUPPORT
7793 		if (cmds[n].wide_func) {
7794 		    printf("%c,", toupper(cmds[n].code));
7795 		}
7796 #endif
7797 		printf("%c\t= %s\n", cmds[n].code, cmds[n].help);
7798 	    }
7799 	}
7800 
7801 	(void) fputs("> ", stdout);
7802 	(void) fflush(stdout);	/* necessary under SVr4 curses */
7803 
7804 	/*
7805 	 * This used to be an 'fgets()' call (until 1996/10).  However with
7806 	 * some runtime libraries, mixing stream I/O and 'read()' causes the
7807 	 * input stream to be flushed when switching between the two.
7808 	 */
7809 	command = 0;
7810 	for (;;) {
7811 	    char ch = '\0';
7812 	    if (read(fileno(stdin), &ch, (size_t) 1) <= 0) {
7813 		int save_err = errno;
7814 		perror("\nOOPS");
7815 		if (save_err == EINTR) {
7816 		    clearerr(stdin);
7817 		    continue;
7818 		} else if (command == 0) {
7819 		    command = 'q';
7820 		}
7821 		break;
7822 	    } else if (command == 0 && !isspace(UChar(ch))) {
7823 		command = ch;
7824 	    } else if (ch == '\n' || ch == '\r') {
7825 		if ((command == 'd') && !top) {
7826 		    (void) fputs("Do not nest test-d\n", stdout);
7827 		    command = 0;
7828 		}
7829 		if (command != 0)
7830 		    break;
7831 		(void) fputs("> ", stdout);
7832 		(void) fflush(stdout);
7833 	    }
7834 	}
7835 
7836 	doit = NULL;
7837 	for (n = 0; n < SIZEOF(cmds); ++n) {
7838 	    if (cmds[n].code == command) {
7839 		doit = cmds[n].narrow_func;
7840 		break;
7841 	    }
7842 #if USE_WIDEC_SUPPORT
7843 	    if (toupper(cmds[n].code) == command) {
7844 		doit = cmds[n].wide_func;
7845 		break;
7846 	    }
7847 #endif
7848 	}
7849 
7850 	if (doit != NULL && doit(FALSE) == OK) {
7851 	    /*
7852 	     * This may be overkill; it is intended to reset everything back
7853 	     * to the initial terminal modes so that tests don't get in
7854 	     * each other's way.
7855 	     */
7856 	    flushinp();
7857 	    set_terminal_modes();
7858 	    reset_prog_mode();
7859 	    clear();
7860 	    refresh();
7861 	    endwin();
7862 	    if (command == '?') {
7863 		(void) puts("This is the ncurses capability tester.");
7864 		(void)
7865 		    puts("You may select a test from the main menu by typing the");
7866 		(void)
7867 		    puts("key letter of the choice (the letter to left of the =)");
7868 		(void)
7869 		    puts("at the > prompt.  Type `q' to exit.");
7870 	    }
7871 	    continue;
7872 	}
7873     } while
7874 	(command != 'q');
7875 }
7876 
7877 /*+-------------------------------------------------------------------------
7878 	main(argc,argv)
7879 --------------------------------------------------------------------------*/
7880 /* *INDENT-OFF* */
VERSION_COMMON()7881 VERSION_COMMON()
7882 /* *INDENT-ON* */
7883 
7884 int
7885 main(int argc, char *argv[])
7886 {
7887     int ch;
7888     int my_e_param = 1;
7889 #ifdef NCURSES_VERSION_PATCH
7890 #if HAVE_USE_DEFAULT_COLORS
7891     int default_fg = COLOR_WHITE;
7892     int default_bg = COLOR_BLACK;
7893     bool default_colors = FALSE;
7894 #if HAVE_ASSUME_DEFAULT_COLORS
7895     bool assumed_colors = FALSE;
7896 #endif
7897 #endif
7898 #endif
7899     bool monochrome = FALSE;
7900 #if HAVE_COLOR_CONTENT
7901     bool xterm_colors = FALSE;
7902     char *palette_file = 0;
7903 #endif
7904 
7905     setlocale(LC_ALL, "");
7906 
7907     while ((ch = getopt(argc, argv, OPTS_COMMON "a:dEe:FHmp:s:Tt:x")) != -1) {
7908 	switch (ch) {
7909 #ifdef NCURSES_VERSION_PATCH
7910 #if HAVE_USE_DEFAULT_COLORS
7911 #if HAVE_ASSUME_DEFAULT_COLORS
7912 	case 'a':
7913 	    assumed_colors = TRUE;
7914 	    switch (sscanf(optarg, "%d,%d", &default_fg, &default_bg)) {
7915 	    case 0:
7916 		default_fg = COLOR_WHITE;
7917 		/* FALLTHRU */
7918 	    case 1:
7919 		default_bg = COLOR_BLACK;
7920 		break;
7921 	    }
7922 	    break;
7923 #endif
7924 	case 'd':
7925 	    default_colors = TRUE;
7926 	    break;
7927 #endif
7928 #endif
7929 #if HAVE_USE_ENV
7930 	case 'E':
7931 	    use_env(FALSE);
7932 	    break;
7933 #endif
7934 	case 'e':
7935 	    my_e_param = atoi(optarg);
7936 #ifdef NCURSES_VERSION
7937 	    if (my_e_param > 3)	/* allow extended layouts */
7938 		usage(FALSE);
7939 #else
7940 	    if (my_e_param > 1)
7941 		usage(FALSE);
7942 #endif
7943 	    break;
7944 #if HAVE_RIPOFFLINE
7945 	case 'F':
7946 	    ripoffline(-1, rip_footer);
7947 	    break;
7948 	case 'H':
7949 	    ripoffline(1, rip_header);
7950 	    break;
7951 #endif /* HAVE_RIPOFFLINE */
7952 	case 'm':
7953 	    monochrome = TRUE;
7954 	    break;
7955 #if HAVE_COLOR_CONTENT
7956 	case 'p':
7957 	    palette_file = optarg;
7958 	    break;
7959 #endif
7960 #if USE_LIBPANEL
7961 	case 's':
7962 	    nap_msec = (int) atol(optarg);
7963 	    break;
7964 #endif
7965 #if defined(NCURSES_VERSION_PATCH) && (NCURSES_VERSION_PATCH >= 20120714) && !defined(_NC_WINDOWS)
7966 	case 'T':
7967 	    use_tioctl(TRUE);
7968 	    break;
7969 #endif
7970 #ifdef TRACE
7971 	case 't':
7972 	    save_trace = (unsigned) strtol(optarg, 0, 0);
7973 	    break;
7974 #endif
7975 #if HAVE_COLOR_CONTENT
7976 	case 'x':
7977 	    xterm_colors = TRUE;
7978 	    break;
7979 #endif
7980 	case OPTS_VERSION:
7981 	    show_version(argv);
7982 	    ExitProgram(EXIT_SUCCESS);
7983 	default:
7984 	    usage(ch == OPTS_USAGE);
7985 	    /* NOTREACHED */
7986 	}
7987     }
7988 
7989     /*
7990      * If there's no menus (unlikely for ncurses!), then we'll have to set
7991      * tracing on initially, just in case the user wants to test something that
7992      * doesn't involve wGetchar.
7993      */
7994 #ifdef TRACE
7995     /* enable debugging */
7996 #if !USE_LIBMENU
7997     curses_trace(save_trace);
7998 #else
7999     if (!isatty(fileno(stdin)))
8000 	curses_trace(save_trace);
8001 #endif /* USE_LIBMENU */
8002 #endif /* TRACE */
8003 
8004 #if USE_SOFTKEYS
8005     /* tell it we're going to play with soft keys */
8006     slk_init(my_e_param);
8007 #endif
8008 
8009 #ifdef SIGUSR1
8010     /* set up null signal catcher so we can see what interrupts to getch do */
8011     signal(SIGUSR1, announce_sig);
8012 #endif
8013 
8014     /* we must initialize the curses data structure only once */
8015     initscr();
8016     bkgdset(BLANK);
8017 
8018     set_terminal_modes();
8019     def_prog_mode();
8020 
8021     /* tests, in general, will want these modes */
8022     UseColors = (bool) (monochrome ? FALSE : has_colors());
8023 
8024     if (UseColors) {
8025 	start_color();
8026 #ifdef NCURSES_VERSION_PATCH
8027 	MaxColors = COLORS;	/* was > 16 ? 16 : COLORS */
8028 #if HAVE_USE_DEFAULT_COLORS
8029 	if (default_colors) {
8030 	    use_default_colors();
8031 	    MinColors = -1;
8032 	}
8033 #if HAVE_ASSUME_DEFAULT_COLORS
8034 	if (assumed_colors)
8035 	    assume_default_colors(default_fg, default_bg);
8036 #endif
8037 #endif
8038 #else /* normal SVr4 curses */
8039 	MaxColors = COLORS;	/* was > 8 ? 8 : COLORS */
8040 #endif
8041 	max_pairs = COLOR_PAIRS;	/* was > 256 ? 256 : COLOR_PAIRS */
8042 
8043 #if HAVE_COLOR_CONTENT
8044 	if (can_change_color()) {
8045 	    init_all_colors(xterm_colors, palette_file);
8046 	}
8047 #endif
8048     }
8049 
8050     /*
8051      * Return to terminal mode, so we're guaranteed of being able to
8052      * select terminal commands even if the capabilities are wrong.
8053      */
8054     endwin();
8055 
8056 #if HAVE_CURSES_VERSION
8057     (void) printf("Welcome to %s.  Press ? for help.\n", curses_version());
8058 #elif defined(NCURSES_VERSION_MAJOR) && defined(NCURSES_VERSION_MINOR) && defined(NCURSES_VERSION_PATCH)
8059     (void) printf("Welcome to ncurses %d.%d.%d.  Press ? for help.\n",
8060 		  NCURSES_VERSION_MAJOR,
8061 		  NCURSES_VERSION_MINOR,
8062 		  NCURSES_VERSION_PATCH);
8063 #else
8064     (void) puts("Welcome to ncurses.  Press ? for help.");
8065 #endif
8066 
8067     main_menu(TRUE);
8068 
8069     ExitProgram(EXIT_SUCCESS);
8070 }
8071 
8072 /* ncurses.c ends here */
8073