blob: 3fd7dfae705b07c06f4dac383f55e09da374b94b [file] [log] [blame]
/****************************************************************************
* Copyright (c) 1998-2007,2008 Free Software Foundation, Inc. *
* *
* Permission is hereby granted, free of charge, to any person obtaining a *
* copy of this software and associated documentation files (the *
* "Software"), to deal in the Software without restriction, including *
* without limitation the rights to use, copy, modify, merge, publish, *
* distribute, distribute with modifications, sublicense, and/or sell *
* copies of the Software, and to permit persons to whom the Software is *
* furnished to do so, subject to the following conditions: *
* *
* The above copyright notice and this permission notice shall be included *
* in all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
* IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *
* THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
* *
* Except as contained in this notice, the name(s) of the above copyright *
* holders shall not be used in advertising or otherwise to promote the *
* sale, use or other dealings in this Software without prior written *
* authorization. *
****************************************************************************/
/****************************************************************************
NAME
ncurses.c --- ncurses library exerciser
SYNOPSIS
ncurses
DESCRIPTION
An interactive test module for the ncurses library.
AUTHOR
Author: Eric S. Raymond <esr@snark.thyrsus.com> 1993
Thomas E. Dickey (beginning revision 1.27 in 1996).
$Id: ncurses.c,v 1.329 2008/09/27 14:34:58 tom Exp $
***************************************************************************/
#include <test.priv.h>
#ifdef __hpux
#undef mvwdelch /* HPUX 11.23 macro will not compile */
#endif
#if HAVE_GETTIMEOFDAY
#if HAVE_SYS_TIME_H && HAVE_SYS_TIME_SELECT
#include <sys/time.h>
#endif
#if HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#endif
#if USE_LIBPANEL
#include <panel.h>
#endif
#if USE_LIBMENU
#include <menu.h>
#endif
#if USE_LIBFORM
#include <form.h>
#endif
#ifdef NCURSES_VERSION
#define NCURSES_CONST_PARAM const void
#ifdef TRACE
static unsigned save_trace = TRACE_ORDINARY | TRACE_ICALLS | TRACE_CALLS;
extern unsigned _nc_tracing;
#endif
#else
#define NCURSES_CONST_PARAM char
#define mmask_t chtype /* not specified in XSI */
#ifndef ACS_S3
#ifdef CURSES_ACS_ARRAY
#define ACS_S3 (CURSES_ACS_ARRAY['p']) /* scan line 3 */
#define ACS_S7 (CURSES_ACS_ARRAY['r']) /* scan line 7 */
#define ACS_LEQUAL (CURSES_ACS_ARRAY['y']) /* less/equal */
#define ACS_GEQUAL (CURSES_ACS_ARRAY['z']) /* greater/equal */
#define ACS_PI (CURSES_ACS_ARRAY['{']) /* Pi */
#define ACS_NEQUAL (CURSES_ACS_ARRAY['|']) /* not equal */
#define ACS_STERLING (CURSES_ACS_ARRAY['}']) /* UK pound sign */
#else
#define ACS_S3 (A_ALTCHARSET + 'p') /* scan line 3 */
#define ACS_S7 (A_ALTCHARSET + 'r') /* scan line 7 */
#define ACS_LEQUAL (A_ALTCHARSET + 'y') /* less/equal */
#define ACS_GEQUAL (A_ALTCHARSET + 'z') /* greater/equal */
#define ACS_PI (A_ALTCHARSET + '{') /* Pi */
#define ACS_NEQUAL (A_ALTCHARSET + '|') /* not equal */
#define ACS_STERLING (A_ALTCHARSET + '}') /* UK pound sign */
#endif
#endif /* ACS_S3 */
#ifdef CURSES_WACS_ARRAY
#define WACS_S3 (&(CURSES_WACS_ARRAY['p'])) /* scan line 3 */
#define WACS_S7 (&(CURSES_WACS_ARRAY['r'])) /* scan line 7 */
#define WACS_LEQUAL (&(CURSES_WACS_ARRAY['y'])) /* less/equal */
#define WACS_GEQUAL (&(CURSES_WACS_ARRAY['z'])) /* greater/equal */
#define WACS_PI (&(CURSES_WACS_ARRAY['{'])) /* Pi */
#define WACS_NEQUAL (&(CURSES_WACS_ARRAY['|'])) /* not equal */
#define WACS_STERLING (&(CURSES_WACS_ARRAY['}'])) /* UK pound sign */
#endif
#endif
#if HAVE_WCSRTOMBS
#define count_wchars(src, len, state) wcsrtombs(0, &src, len, state)
#define trans_wchars(dst, src, len, state) wcsrtombs(dst, &src, len, state)
#define reset_wchars(state) memset(&state, 0, sizeof(state))
#elif HAVE_WCSTOMBS && HAVE_MBTOWC && HAVE_MBLEN
#define count_wchars(src, len, state) wcstombs(0, src, len)
#define trans_wchars(dst, src, len, state) wcstombs(dst, src, len)
#define reset_wchars(state) mblen(NULL, 0), mbtowc(NULL, NULL, 0)
#define state_unused
#endif
#if HAVE_MBSRTOWCS
#define count_mbytes(src, len, state) mbsrtowcs(0, &src, len, state)
#define trans_mbytes(dst, src, len, state) mbsrtowcs(dst, &src, len, state)
#define reset_mbytes(state) memset(&state, 0, sizeof(state))
#elif HAVE_MBSTOWCS && HAVE_MBTOWC && HAVE_MBLEN
#define count_mbytes(src, len, state) mbstowcs(0, src, len)
#define trans_mbytes(dst, src, len, state) mbstowcs(dst, src, len)
#define reset_mbytes(state) mblen(NULL, 0), mbtowc(NULL, NULL, 0)
#define state_unused
#endif
#define ToggleAcs(temp,real) temp = ((temp == real) ? 0 : real)
#define P(string) printw("%s\n", string)
#define BLANK ' ' /* this is the background character */
#undef max_colors
static int max_colors; /* the actual number of colors we'll use */
static int min_colors; /* the minimum color code */
static bool use_colors; /* true if we use colors */
#undef max_pairs
static int max_pairs; /* ...and the number of color pairs */
typedef struct {
short red;
short green;
short blue;
} RGB_DATA;
static RGB_DATA *all_colors;
static void main_menu(bool);
/* The behavior of mvhline, mvvline for negative/zero length is unspecified,
* though we can rely on negative x/y values to stop the macro.
*/
static void
do_h_line(int y, int x, chtype c, int to)
{
if ((to) > (x))
mvhline(y, x, c, (to) - (x));
}
static void
do_v_line(int y, int x, chtype c, int to)
{
if ((to) > (y))
mvvline(y, x, c, (to) - (y));
}
static void
Repaint(void)
{
touchwin(stdscr);
touchwin(curscr);
wrefresh(curscr);
}
static bool
isQuit(int c)
{
return ((c) == QUIT || (c) == ESCAPE);
}
#define case_QUIT QUIT: case ESCAPE
/* Common function to allow ^T to toggle trace-mode in the middle of a test
* so that trace-files can be made smaller.
*/
static int
wGetchar(WINDOW *win)
{
int c;
#ifdef TRACE
while ((c = wgetch(win)) == CTRL('T')) {
if (_nc_tracing) {
save_trace = _nc_tracing;
Trace(("TOGGLE-TRACING OFF"));
_nc_tracing = 0;
} else {
_nc_tracing = save_trace;
}
trace(_nc_tracing);
if (_nc_tracing)
Trace(("TOGGLE-TRACING ON"));
}
#else
c = wgetch(win);
#endif
return c;
}
#define Getchar() wGetchar(stdscr)
/* replaces wgetnstr(), since we want to be able to edit values */
static void
wGetstring(WINDOW *win, char *buffer, int limit)
{
int y0, x0, x, ch;
bool done = FALSE;
echo();
getyx(win, y0, x0);
wattrset(win, A_REVERSE);
x = (int) strlen(buffer);
while (!done) {
if (x > (int) strlen(buffer))
x = (int) strlen(buffer);
wmove(win, y0, x0);
wprintw(win, "%-*s", limit, buffer);
wmove(win, y0, x0 + x);
switch (ch = wGetchar(win)) {
case '\n':
case KEY_ENTER:
done = TRUE;
break;
case CTRL('U'):
*buffer = '\0';
break;
case '\b':
case KEY_BACKSPACE:
case KEY_DC:
if (x > 0) {
int j;
for (j = --x; (buffer[j] = buffer[j + 1]) != '\0'; ++j) {
;
}
} else {
beep();
}
break;
case KEY_LEFT:
if (x > 0) {
--x;
} else {
flash();
}
break;
case KEY_RIGHT:
++x;
break;
default:
if (!isprint(ch) || ch >= KEY_MIN) {
beep();
} else if ((int) strlen(buffer) < limit) {
int j;
for (j = (int) strlen(buffer) + 1; j > x; --j) {
buffer[j] = buffer[j - 1];
}
buffer[x++] = (char) ch;
} else {
flash();
}
}
}
wattroff(win, A_REVERSE);
wmove(win, y0, x0);
noecho();
}
#if USE_WIDEC_SUPPORT
static wchar_t
fullwidth_of(int ch)
{
return (ch + 0xff10 - '0');
}
static void
make_fullwidth_text(wchar_t *target, const char *source)
{
int ch;
while ((ch = *source++) != 0) {
*target++ = fullwidth_of(ch);
}
*target = 0;
}
static void
make_narrow_text(wchar_t *target, const char *source)
{
int ch;
while ((ch = *source++) != 0) {
*target++ = ch;
}
*target = 0;
}
static void
make_fullwidth_digit(cchar_t *target, int digit)
{
wchar_t source[2];
source[0] = fullwidth_of(digit + '0');
source[1] = 0;
setcchar(target, source, A_NORMAL, 0, 0);
}
static int
wGet_wchar(WINDOW *win, wint_t *result)
{
int c;
#ifdef TRACE
while ((c = wget_wch(win, result)) == CTRL('T')) {
if (_nc_tracing) {
save_trace = _nc_tracing;
Trace(("TOGGLE-TRACING OFF"));
_nc_tracing = 0;
} else {
_nc_tracing = save_trace;
}
trace(_nc_tracing);
if (_nc_tracing)
Trace(("TOGGLE-TRACING ON"));
}
#else
c = wget_wch(win, result);
#endif
return c;
}
#define Get_wchar(result) wGet_wchar(stdscr, result)
/* replaces wgetn_wstr(), since we want to be able to edit values */
static void
wGet_wstring(WINDOW *win, wchar_t *buffer, int limit)
{
int y0, x0, x;
wint_t ch;
bool done = FALSE;
bool fkey = FALSE;
echo();
getyx(win, y0, x0);
wattrset(win, A_REVERSE);
x = (int) wcslen(buffer);
while (!done) {
if (x > (int) wcslen(buffer))
x = (int) wcslen(buffer);
/* clear the "window' */
wmove(win, y0, x0);
wprintw(win, "%*s", limit, " ");
/* write the existing buffer contents */
wmove(win, y0, x0);
waddnwstr(win, buffer, limit);
/* positions the cursor past character 'x' */
wmove(win, y0, x0);
waddnwstr(win, buffer, x);
switch (wGet_wchar(win, &ch)) {
case KEY_CODE_YES:
fkey = TRUE;
switch (ch) {
case KEY_ENTER:
ch = '\n';
fkey = FALSE;
break;
case KEY_BACKSPACE:
case KEY_DC:
ch = '\b';
fkey = FALSE;
break;
case KEY_LEFT:
case KEY_RIGHT:
break;
default:
ch = (wint_t) -1;
break;
}
break;
case OK:
fkey = FALSE;
break;
default:
ch = (wint_t) -1;
fkey = TRUE;
break;
}
switch (ch) {
case '\n':
done = TRUE;
break;
case CTRL('U'):
*buffer = '\0';
break;
case '\b':
if (x > 0) {
int j;
for (j = --x; (buffer[j] = buffer[j + 1]) != '\0'; ++j) {
;
}
} else {
beep();
}
break;
case KEY_LEFT:
if (x > 0) {
--x;
} else {
beep();
}
break;
case KEY_RIGHT:
++x;
break;
default:
if (fkey) {
beep();
} else if ((int) wcslen(buffer) < limit) {
int j;
for (j = (int) wcslen(buffer) + 1; j > x; --j) {
buffer[j] = buffer[j - 1];
}
buffer[x++] = (wchar_t) ch;
} else {
beep();
}
}
}
wattroff(win, A_REVERSE);
wmove(win, y0, x0);
noecho();
}
#endif
static void
Pause(void)
{
move(LINES - 1, 0);
addstr("Press any key to continue... ");
(void) Getchar();
}
static void
Cannot(const char *what)
{
printw("\nThis %s terminal %s\n\n", getenv("TERM"), what);
Pause();
}
static void
ShellOut(bool message)
{
if (message)
addstr("Shelling out...");
def_prog_mode();
endwin();
system("sh");
if (message)
addstr("returned from shellout.\n");
refresh();
}
#ifdef NCURSES_MOUSE_VERSION
/*
* This function is the same as _tracemouse(), but we cannot count on that
* being available in the non-debug library.
*/
static const char *
mouse_decode(MEVENT const *ep)
{
static char buf[80 + (5 * 10) + (32 * 15)];
(void) sprintf(buf, "id %2d at (%2d, %2d, %2d) state %4lx = {",
ep->id, ep->x, ep->y, ep->z, (unsigned long) ep->bstate);
#define SHOW(m, s) if ((ep->bstate & m)==m) {strcat(buf,s); strcat(buf, ", ");}
SHOW(BUTTON1_RELEASED, "release-1");
SHOW(BUTTON1_PRESSED, "press-1");
SHOW(BUTTON1_CLICKED, "click-1");
SHOW(BUTTON1_DOUBLE_CLICKED, "doubleclick-1");
SHOW(BUTTON1_TRIPLE_CLICKED, "tripleclick-1");
#if NCURSES_MOUSE_VERSION == 1
SHOW(BUTTON1_RESERVED_EVENT, "reserved-1");
#endif
SHOW(BUTTON2_RELEASED, "release-2");
SHOW(BUTTON2_PRESSED, "press-2");
SHOW(BUTTON2_CLICKED, "click-2");
SHOW(BUTTON2_DOUBLE_CLICKED, "doubleclick-2");
SHOW(BUTTON2_TRIPLE_CLICKED, "tripleclick-2");
#if NCURSES_MOUSE_VERSION == 1
SHOW(BUTTON2_RESERVED_EVENT, "reserved-2");
#endif
SHOW(BUTTON3_RELEASED, "release-3");
SHOW(BUTTON3_PRESSED, "press-3");
SHOW(BUTTON3_CLICKED, "click-3");
SHOW(BUTTON3_DOUBLE_CLICKED, "doubleclick-3");
SHOW(BUTTON3_TRIPLE_CLICKED, "tripleclick-3");
#if NCURSES_MOUSE_VERSION == 1
SHOW(BUTTON3_RESERVED_EVENT, "reserved-3");
#endif
SHOW(BUTTON4_RELEASED, "release-4");
SHOW(BUTTON4_PRESSED, "press-4");
SHOW(BUTTON4_CLICKED, "click-4");
SHOW(BUTTON4_DOUBLE_CLICKED, "doubleclick-4");
SHOW(BUTTON4_TRIPLE_CLICKED, "tripleclick-4");
#if NCURSES_MOUSE_VERSION == 1
SHOW(BUTTON4_RESERVED_EVENT, "reserved-4");
#endif
#if NCURSES_MOUSE_VERSION == 2
SHOW(BUTTON5_RELEASED, "release-5");
SHOW(BUTTON5_PRESSED, "press-5");
SHOW(BUTTON5_CLICKED, "click-5");
SHOW(BUTTON5_DOUBLE_CLICKED, "doubleclick-5");
SHOW(BUTTON5_TRIPLE_CLICKED, "tripleclick-5");
#endif
SHOW(BUTTON_CTRL, "ctrl");
SHOW(BUTTON_SHIFT, "shift");
SHOW(BUTTON_ALT, "alt");
SHOW(ALL_MOUSE_EVENTS, "all-events");
SHOW(REPORT_MOUSE_POSITION, "position");
#undef SHOW
if (buf[strlen(buf) - 1] == ' ')
buf[strlen(buf) - 2] = '\0';
(void) strcat(buf, "}");
return (buf);
}
#endif /* NCURSES_MOUSE_VERSION */
/****************************************************************************
*
* Character input test
*
****************************************************************************/
static void
setup_getch(WINDOW *win, bool flags[])
{
keypad(win, flags['k']); /* should be redundant, but for testing */
meta(win, flags['m']); /* force this to a known state */
if (flags['e'])
echo();
else
noecho();
}
static void
wgetch_help(WINDOW *win, bool flags[])
{
static const char *help[] =
{
"e -- toggle echo mode"
,"g -- triggers a getstr test"
,"k -- toggle keypad/literal mode"
,"m -- toggle meta (7-bit/8-bit) mode"
,"^q -- quit"
,"s -- shell out\n"
,"w -- create a new window"
#ifdef SIGTSTP
,"z -- suspend this process"
#endif
};
int y, x;
unsigned chk = ((SIZEOF(help) + 1) / 2);
unsigned n;
getyx(win, y, x);
move(0, 0);
printw("Type any key to see its %s value. Also:\n",
flags['k'] ? "keypad" : "literal");
for (n = 0; n < SIZEOF(help); ++n) {
int row = 1 + (int) (n % chk);
int col = (n >= chk) ? COLS / 2 : 0;
int flg = ((strstr(help[n], "toggle") != 0)
&& (flags[UChar(*help[n])] != FALSE));
if (flg)
standout();
mvprintw(row, col, "%s", help[n]);
if (col == 0)
clrtoeol();
if (flg)
standend();
}
wrefresh(stdscr);
wmove(win, y, x);
}
static void
wgetch_wrap(WINDOW *win, int first_y)
{
int last_y = getmaxy(win) - 1;
int y = getcury(win) + 1;
if (y >= last_y)
y = first_y;
wmove(win, y, 0);
wclrtoeol(win);
}
#if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
typedef struct {
WINDOW *text;
WINDOW *frame;
} WINSTACK;
static WINSTACK *winstack = 0;
static unsigned len_winstack = 0;
static void
forget_boxes(void)
{
if (winstack != 0) {
free(winstack);
}
winstack = 0;
len_winstack = 0;
}
static void
remember_boxes(unsigned level, WINDOW *txt_win, WINDOW *box_win)
{
unsigned need = (level + 1) * 2;
assert(level < COLS);
if (winstack == 0) {
len_winstack = 20;
winstack = typeMalloc(WINSTACK, len_winstack);
} else if (need >= len_winstack) {
len_winstack = need;
winstack = typeRealloc(WINSTACK, len_winstack, winstack);
}
winstack[level].text = txt_win;
winstack[level].frame = box_win;
}
#if USE_SOFTKEYS && (NCURSES_VERSION_PATCH < 20071229) && NCURSES_EXT_FUNCS
static void
slk_repaint(void)
{
/* this chunk is now done in resize_term() */
slk_touch();
slk_clear();
slk_noutrefresh();
}
#else
#define slk_repaint() /* nothing */
#endif
/*
* For wgetch_test(), we create pairs of windows - one for a box, one for text.
* Resize both and paint the box in the parent.
*/
static void
resize_boxes(unsigned level, WINDOW *win)
{
unsigned n;
int base = 5;
int high = LINES - base;
int wide = COLS;
touchwin(stdscr);
wnoutrefresh(stdscr);
slk_repaint();
for (n = 0; n < level; ++n) {
wresize(winstack[n].frame, high, wide);
wresize(winstack[n].text, high - 2, wide - 2);
high -= 2;
wide -= 2;
werase(winstack[n].text);
box(winstack[n].frame, 0, 0);
wnoutrefresh(winstack[n].frame);
wprintw(winstack[n].text,
"size %dx%d\n",
getmaxy(winstack[n].text),
getmaxx(winstack[n].text));
wnoutrefresh(winstack[n].text);
if (winstack[n].text == win)
break;
}
doupdate();
}
#else
#define forget_boxes() /* nothing */
#define remember_boxes(level,text,frame) /* nothing */
#endif
static void
wgetch_test(unsigned level, WINDOW *win, int delay)
{
char buf[BUFSIZ];
int first_y, first_x;
int c;
int incount = 0;
bool flags[256];
bool blocking = (delay < 0);
memset(flags, FALSE, sizeof(flags));
flags[UChar('k')] = (win == stdscr);
setup_getch(win, flags);
wtimeout(win, delay);
getyx(win, first_y, first_x);
wgetch_help(win, flags);
wsetscrreg(win, first_y, getmaxy(win) - 1);
scrollok(win, TRUE);
for (;;) {
while ((c = wGetchar(win)) == ERR) {
incount++;
if (blocking) {
(void) wprintw(win, "%05d: input error", incount);
break;
} else {
(void) wprintw(win, "%05d: input timed out", incount);
}
wgetch_wrap(win, first_y);
}
if (c == ERR && blocking) {
wprintw(win, "ERR");
wgetch_wrap(win, first_y);
} else if (isQuit(c)) {
break;
} else if (c == 'e') {
flags[UChar('e')] = !flags[UChar('e')];
setup_getch(win, flags);
wgetch_help(win, flags);
} else if (c == 'g') {
waddstr(win, "getstr test: ");
echo();
wgetnstr(win, buf, sizeof(buf) - 1);
noecho();
wprintw(win, "I saw %d characters:\n\t`%s'.", (int) strlen(buf), buf);
wclrtoeol(win);
wgetch_wrap(win, first_y);
} else if (c == 'k') {
flags[UChar('k')] = !flags[UChar('k')];
setup_getch(win, flags);
wgetch_help(win, flags);
} else if (c == 'm') {
flags[UChar('m')] = !flags[UChar('m')];
setup_getch(win, flags);
wgetch_help(win, flags);
} else if (c == 's') {
ShellOut(TRUE);
} else if (c == 'w') {
int high = getmaxy(win) - 1 - first_y + 1;
int wide = getmaxx(win) - first_x;
int old_y, old_x;
int new_y = first_y + getbegy(win);
int new_x = first_x + getbegx(win);
getyx(win, old_y, old_x);
if (high > 2 && wide > 2) {
WINDOW *wb = newwin(high, wide, new_y, new_x);
WINDOW *wi = newwin(high - 2, wide - 2, new_y + 1, new_x + 1);
box(wb, 0, 0);
wrefresh(wb);
wmove(wi, 0, 0);
remember_boxes(level, wi, wb);
wgetch_test(level + 1, wi, delay);
delwin(wi);
delwin(wb);
wgetch_help(win, flags);
wmove(win, old_y, old_x);
touchwin(win);
wrefresh(win);
doupdate();
}
#ifdef SIGTSTP
} else if (c == 'z') {
kill(getpid(), SIGTSTP);
#endif
} else {
wprintw(win, "Key pressed: %04o ", c);
#ifdef NCURSES_MOUSE_VERSION
if (c == KEY_MOUSE) {
int y, x;
MEVENT event;
getmouse(&event);
wprintw(win, "KEY_MOUSE, %s", mouse_decode(&event));
getyx(win, y, x);
move(event.y, event.x);
addch('*');
wmove(win, y, x);
} else
#endif /* NCURSES_MOUSE_VERSION */
if (c >= KEY_MIN) {
#if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
if (c == KEY_RESIZE) {
resize_boxes(level, win);
}
#endif
(void) waddstr(win, keyname(c));
} else if (c > 0x80) {
unsigned c2 = (unsigned) (c & 0x7f);
if (isprint(c2))
(void) wprintw(win, "M-%c", UChar(c2));
else
(void) wprintw(win, "M-%s", unctrl(c2));
waddstr(win, " (high-half character)");
} else {
if (isprint(c))
(void) wprintw(win, "%c (ASCII printable character)", c);
else
(void) wprintw(win, "%s (ASCII control character)",
unctrl(UChar(c)));
}
wgetch_wrap(win, first_y);
}
}
wtimeout(win, -1);
}
static int
begin_getch_test(void)
{
char buf[BUFSIZ];
int delay;
refresh();
#ifdef NCURSES_MOUSE_VERSION
mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
#endif
(void) printw("Delay in 10ths of a second (<CR> for blocking input)? ");
echo();
getnstr(buf, sizeof(buf) - 1);
noecho();
nonl();
if (isdigit(UChar(buf[0]))) {
delay = atoi(buf) * 100;
} else {
delay = -1;
}
raw();
move(5, 0);
return delay;
}
static void
finish_getch_test(void)
{
#ifdef NCURSES_MOUSE_VERSION
mousemask(0, (mmask_t *) 0);
#endif
erase();
noraw();
nl();
endwin();
}
static void
getch_test(void)
{
int delay = begin_getch_test();
slk_restore();
wgetch_test(0, stdscr, delay);
forget_boxes();
finish_getch_test();
}
#if USE_WIDEC_SUPPORT
/*
* For wget_wch_test(), we create pairs of windows - one for a box, one for text.
* Resize both and paint the box in the parent.
*/
#if defined(KEY_RESIZE) && HAVE_WRESIZE
static void
resize_wide_boxes(unsigned level, WINDOW *win)
{
unsigned n;
int base = 5;
int high = LINES - base;
int wide = COLS;
touchwin(stdscr);
wnoutrefresh(stdscr);
slk_repaint();
for (n = 0; n < level; ++n) {
wresize(winstack[n].frame, high, wide);
wresize(winstack[n].text, high - 2, wide - 2);
high -= 2;
wide -= 2;
werase(winstack[n].text);
box_set(winstack[n].frame, 0, 0);
wnoutrefresh(winstack[n].frame);
wprintw(winstack[n].text,
"size %dx%d\n",
getmaxy(winstack[n].text),
getmaxx(winstack[n].text));
wnoutrefresh(winstack[n].text);
if (winstack[n].text == win)
break;
}
doupdate();
}
#endif /* KEY_RESIZE */
static char *
wcstos(const wchar_t *src)
{
int need;
char *result = 0;
const wchar_t *tmp = src;
#ifndef state_unused
mbstate_t state;
#endif
reset_wchars(state);
if ((need = (int) count_wchars(tmp, 0, &state)) > 0) {
unsigned have = (unsigned) need;
if ((result = typeCalloc(char, have + 1)) != 0) {
tmp = src;
if (trans_wchars(result, tmp, have, &state) != have) {
free(result);
result = 0;
}
}
}
return result;
}
static void
wget_wch_test(unsigned level, WINDOW *win, int delay)
{
wchar_t wchar_buf[BUFSIZ];
wint_t wint_buf[BUFSIZ];
int first_y, first_x;
wint_t c;
int incount = 0;
bool flags[256];
bool blocking = (delay < 0);
int y, x, code;
char *temp;
memset(flags, FALSE, sizeof(flags));
flags[UChar('k')] = (win == stdscr);
setup_getch(win, flags);
wtimeout(win, delay);
getyx(win, first_y, first_x);
wgetch_help(win, flags);
wsetscrreg(win, first_y, getmaxy(win) - 1);
scrollok(win, TRUE);
for (;;) {
while ((code = wGet_wchar(win, &c)) == ERR) {
incount++;
if (blocking) {
(void) wprintw(win, "%05d: input error", incount);
break;
} else {
(void) wprintw(win, "%05d: input timed out", incount);
}
wgetch_wrap(win, first_y);
}
if (code == ERR && blocking) {
wprintw(win, "ERR");
wgetch_wrap(win, first_y);
} else if (isQuit((int) c)) {
break;
} else if (c == 'e') {
flags[UChar('e')] = !flags[UChar('e')];
setup_getch(win, flags);
wgetch_help(win, flags);
} else if (c == 'g') {
waddstr(win, "getstr test: ");
echo();
code = wgetn_wstr(win, wint_buf, sizeof(wint_buf) - 1);
noecho();
if (code == ERR) {
wprintw(win, "wgetn_wstr returns an error.");
} else {
int n;
for (n = 0; (wchar_buf[n] = (wchar_t) wint_buf[n]) != 0; ++n) {
;
}
if ((temp = wcstos(wchar_buf)) != 0) {
wprintw(win, "I saw %d characters:\n\t`%s'.",
(int) wcslen(wchar_buf), temp);
free(temp);
} else {
wprintw(win, "I saw %d characters (cannot convert).",
(int) wcslen(wchar_buf));
}
}
wclrtoeol(win);
wgetch_wrap(win, first_y);
} else if (c == 'k') {
flags[UChar('k')] = !flags[UChar('k')];
setup_getch(win, flags);
wgetch_help(win, flags);
} else if (c == 'm') {
flags[UChar('m')] = !flags[UChar('m')];
setup_getch(win, flags);
wgetch_help(win, flags);
} else if (c == 's') {
ShellOut(TRUE);
} else if (c == 'w') {
int high = getmaxy(win) - 1 - first_y + 1;
int wide = getmaxx(win) - first_x;
int old_y, old_x;
int new_y = first_y + getbegy(win);
int new_x = first_x + getbegx(win);
getyx(win, old_y, old_x);
if (high > 2 && wide > 2) {
WINDOW *wb = newwin(high, wide, new_y, new_x);
WINDOW *wi = newwin(high - 2, wide - 2, new_y + 1, new_x + 1);
box_set(wb, 0, 0);
wrefresh(wb);
wmove(wi, 0, 0);
remember_boxes(level, wi, wb);
wget_wch_test(level + 1, wi, delay);
delwin(wi);
delwin(wb);
wgetch_help(win, flags);
wmove(win, old_y, old_x);
touchwin(win);
wrefresh(win);
}
#ifdef SIGTSTP
} else if (c == 'z') {
kill(getpid(), SIGTSTP);
#endif
} else {
wprintw(win, "Key pressed: %04o ", (int) c);
#ifdef NCURSES_MOUSE_VERSION
if (c == KEY_MOUSE) {
MEVENT event;
getmouse(&event);
wprintw(win, "KEY_MOUSE, %s", mouse_decode(&event));
getyx(win, y, x);
move(event.y, event.x);
addch('*');
wmove(win, y, x);
} else
#endif /* NCURSES_MOUSE_VERSION */
if (code == KEY_CODE_YES) {
#if defined(KEY_RESIZE) && HAVE_WRESIZE
if (c == KEY_RESIZE) {
resize_wide_boxes(level, win);
}
#endif
(void) waddstr(win, key_name((wchar_t) c));
} else {
if (c < 256 && iscntrl(c)) {
(void) wprintw(win, "%s (control character)", unctrl(c));
} else {
wchar_t c2 = (wchar_t) c;
waddnwstr(win, &c2, 1);
(void) wprintw(win, " = %#x (printable character)", (unsigned) c);
}
}
wgetch_wrap(win, first_y);
}
}
wtimeout(win, -1);
}
static void
get_wch_test(void)
{
int delay = begin_getch_test();
slk_restore();
wget_wch_test(0, stdscr, delay);
forget_boxes();
finish_getch_test();
}
#endif
/****************************************************************************
*
* Character attributes test
*
****************************************************************************/
#if HAVE_SETUPTERM || HAVE_TGETENT
#define get_ncv() TIGETNUM("ncv","NC")
#define get_xmc() TIGETNUM("xmc","sg")
#else
#define get_ncv() -1
#define get_xmc() -1
#endif
#if !HAVE_TERMATTRS
static chtype
my_termattrs(void)
{
static int first = TRUE;
static chtype result = 0;
if (first) {
#if !HAVE_TIGETSTR
char buffer[4096];
char parsed[4096];
char *area_pointer = parsed;
tgetent(buffer, getenv("TERM"));
#endif
if (TIGETSTR("smso", "so"))
result |= A_STANDOUT;
if (TIGETSTR("smul", "us"))
result |= A_UNDERLINE;
if (TIGETSTR("rev", "mr"))
result |= A_REVERSE;
if (TIGETSTR("blink", "mb"))
result |= A_BLINK;
if (TIGETSTR("dim", "mh"))
result |= A_DIM;
if (TIGETSTR("bold", "md"))
result |= A_BOLD;
if (TIGETSTR("smacs", "ac"))
result |= A_ALTCHARSET;
first = FALSE;
}
return result;
}
#define termattrs() my_termattrs()
#endif
#define MAX_ATTRSTRING 31
#define LEN_ATTRSTRING 26
static char attr_test_string[MAX_ATTRSTRING + 1];
static void
attr_legend(WINDOW *helpwin)
{
int row = 1;
int col = 1;
mvwprintw(helpwin, row++, col,
"ESC to exit.");
mvwprintw(helpwin, row++, col,
"^L repaints.");
++row;
mvwprintw(helpwin, row++, col,
"Modify the test strings:");
mvwprintw(helpwin, row++, col,
" A digit sets gaps on each side of displayed attributes");
mvwprintw(helpwin, row++, col,
" </> shifts the text left/right. ");
++row;
mvwprintw(helpwin, row++, col,
"Toggles:");
if (use_colors) {
mvwprintw(helpwin, row++, col,
" f/F/b/F toggle foreground/background background color");
mvwprintw(helpwin, row++, col,
" t/T toggle text/background color attribute");
}
mvwprintw(helpwin, row++, col,
" a/A toggle ACS (alternate character set) mapping");
mvwprintw(helpwin, row++, col,
" v/V toggle video attribute to combine with each line");
}
static void
show_color_attr(int fg, int bg, int tx)
{
if (use_colors) {
printw(" Colors (fg %d, bg %d", fg, bg);
if (tx >= 0)
printw(", text %d", tx);
printw("),");
}
}
static bool
cycle_color_attr(int ch, short *fg, short *bg, short *tx)
{
bool error = FALSE;
if (use_colors) {
switch (ch) {
case 'f':
*fg = (short) (*fg + 1);
break;
case 'F':
*fg = (short) (*fg - 1);
break;
case 'b':
*bg = (short) (*bg + 1);
break;
case 'B':
*bg = (short) (*bg - 1);
break;
case 't':
*tx = (short) (*tx + 1);
break;
case 'T':
*tx = (short) (*tx - 1);
break;
default:
beep();
error = TRUE;
break;
}
if (*fg >= COLORS)
*fg = (short) min_colors;
if (*fg < min_colors)
*fg = (short) (COLORS - 1);
if (*bg >= COLORS)
*bg = (short) min_colors;
if (*bg < min_colors)
*bg = (short) (COLORS - 1);
if (*tx >= COLORS)
*tx = -1;
if (*tx < -1)
*tx = (short) (COLORS - 1);
} else {
beep();
error = TRUE;
}
return error;
}
static void
adjust_attr_string(int adjust)
{
int first = ((int) UChar(attr_test_string[0])) + adjust;
int last = first + LEN_ATTRSTRING;
if (first >= ' ' && last <= '~') { /* 32..126 */
int j, k;
for (j = 0, k = first; j < MAX_ATTRSTRING && k <= last; ++j, ++k) {
attr_test_string[j] = (char) k;
if (((k + 1 - first) % 5) == 0) {
if (++j >= MAX_ATTRSTRING)
break;
attr_test_string[j] = ' ';
}
}
while (j < MAX_ATTRSTRING)
attr_test_string[j++] = ' ';
attr_test_string[j] = '\0';
} else {
beep();
}
}
static void
init_attr_string(void)
{
attr_test_string[0] = 'a';
adjust_attr_string(0);
}
static int
show_attr(int row, int skip, bool arrow, chtype attr, const char *name)
{
int ncv = get_ncv();
chtype test = attr & (chtype) (~A_ALTCHARSET);
if (arrow)
mvprintw(row, 5, "-->");
mvprintw(row, 8, "%s mode:", name);
mvprintw(row, 24, "|");
if (skip)
printw("%*s", skip, " ");
/*
* Just for testing, write text using the alternate character set one
* character at a time (to pass its rendition directly), and use the
* string operation for the other attributes.
*/
if (attr & A_ALTCHARSET) {
const char *s;
chtype ch;
for (s = attr_test_string; *s != '\0'; ++s) {
ch = UChar(*s);
addch(ch | attr);
}
} else {
attrset(attr);
addstr(attr_test_string);
attroff(attr);
}
if (skip)
printw("%*s", skip, " ");
printw("|");
if (test != A_NORMAL) {
if (!(termattrs() & test)) {
printw(" (N/A)");
} else {
if (ncv > 0 && (getbkgd(stdscr) & A_COLOR)) {
static const chtype table[] =
{
A_STANDOUT,
A_UNDERLINE,
A_REVERSE,
A_BLINK,
A_DIM,
A_BOLD,
#ifdef A_INVIS
A_INVIS,
#endif
A_PROTECT,
A_ALTCHARSET
};
unsigned n;
bool found = FALSE;
for (n = 0; n < SIZEOF(table); n++) {
if ((table[n] & attr) != 0
&& ((1 << n) & ncv) != 0) {
found = TRUE;
break;
}
}
if (found)
printw(" (NCV)");
}
if ((termattrs() & test) != test)
printw(" (Part)");
}
}
return row + 2;
}
/* *INDENT-OFF* */
static const struct {
chtype attr;
NCURSES_CONST char * name;
} attrs_to_test[] = {
{ A_STANDOUT, "STANDOUT" },
{ A_REVERSE, "REVERSE" },
{ A_BOLD, "BOLD" },
{ A_UNDERLINE, "UNDERLINE" },
{ A_DIM, "DIM" },
{ A_BLINK, "BLINK" },
{ A_PROTECT, "PROTECT" },
#ifdef A_INVIS
{ A_INVIS, "INVISIBLE" },
#endif
{ A_NORMAL, "NORMAL" },
};
/* *INDENT-ON* */
static bool
attr_getc(int *skip, short *fg, short *bg, short *tx, int *ac, unsigned *kc)
{
bool result = TRUE;
bool error = FALSE;
WINDOW *helpwin;
do {
int ch = Getchar();
error = FALSE;
if (ch < 256 && isdigit(ch)) {
*skip = (ch - '0');
} else {
switch (ch) {
case CTRL('L'):
Repaint();
break;
case '?':
if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
box(helpwin, 0, 0);
attr_legend(helpwin);
wGetchar(helpwin);
delwin(helpwin);
}
break;
case 'a':
*ac = 0;
break;
case 'A':
*ac = A_ALTCHARSET;
break;
case 'v':
if (*kc == 0)
*kc = SIZEOF(attrs_to_test) - 1;
else
*kc -= 1;
break;
case 'V':
*kc += 1;
if (*kc >= SIZEOF(attrs_to_test))
*kc = 0;
break;
case '<':
adjust_attr_string(-1);
break;
case '>':
adjust_attr_string(1);
break;
case case_QUIT:
result = FALSE;
break;
default:
error = cycle_color_attr(ch, fg, bg, tx);
break;
}
}
} while (error);
return result;
}
static void
attr_test(void)
/* test text attributes */
{
int n;
int skip = get_xmc();
short fg = COLOR_BLACK; /* color pair 0 is special */
short bg = COLOR_BLACK;
short tx = -1;
int ac = 0;
unsigned j, k;
if (skip < 0)
skip = 0;
n = skip; /* make it easy */
k = SIZEOF(attrs_to_test) - 1;
init_attr_string();
do {
int row = 2;
chtype normal = A_NORMAL | BLANK;
chtype extras = (chtype) ac;
if (use_colors) {
short pair = (short) (fg != COLOR_BLACK || bg != COLOR_BLACK);
if (pair != 0) {
pair = 1;
if (init_pair(pair, fg, bg) == ERR) {
beep();
} else {
normal |= COLOR_PAIR(pair);
}
}
if (tx >= 0) {
pair = 2;
if (init_pair(pair, tx, bg) == ERR) {
beep();
} else {
extras |= COLOR_PAIR(pair);
}
}
}
bkgd(normal);
bkgdset(normal);
erase();
box(stdscr, 0, 0);
mvaddstr(0, 20, "Character attribute test display");
for (j = 0; j < SIZEOF(attrs_to_test); ++j) {
bool arrow = (j == k);
row = show_attr(row, n, arrow,
extras |
attrs_to_test[j].attr |
attrs_to_test[k].attr,
attrs_to_test[j].name);
}
mvprintw(row, 8,
"This terminal does %shave the magic-cookie glitch",
get_xmc() > -1 ? "" : "not ");
mvprintw(row + 1, 8, "Enter '?' for help.");
show_color_attr(fg, bg, tx);
printw(" ACS (%d)", ac != 0);
refresh();
} while (attr_getc(&n, &fg, &bg, &tx, &ac, &k));
bkgdset(A_NORMAL | BLANK);
erase();
endwin();
}
#if USE_WIDEC_SUPPORT
static wchar_t wide_attr_test_string[MAX_ATTRSTRING + 1];
static void
wide_adjust_attr_string(int adjust)
{
int first = ((int) UChar(wide_attr_test_string[0])) + adjust;
int last = first + LEN_ATTRSTRING;
if (first >= ' ' && last <= '~') { /* 32..126 */
int j, k;
for (j = 0, k = first; j < MAX_ATTRSTRING && k <= last; ++j, ++k) {
wide_attr_test_string[j] = k;
if (((k + 1 - first) % 5) == 0) {
if (++j >= MAX_ATTRSTRING)
break;
wide_attr_test_string[j] = ' ';
}
}
while (j < MAX_ATTRSTRING)
wide_attr_test_string[j++] = ' ';
wide_attr_test_string[j] = '\0';
} else {
beep();
}
}
static void
wide_init_attr_string(void)
{
wide_attr_test_string[0] = 'a';
wide_adjust_attr_string(0);
}
static void
set_wide_background(short pair)
{
cchar_t normal;
wchar_t blank[2];
blank[0] = ' ';
blank[1] = 0;
setcchar(&normal, blank, A_NORMAL, pair, 0);
bkgrnd(&normal);
bkgrndset(&normal);
}
static attr_t
get_wide_background(void)
{
attr_t result = A_NORMAL;
attr_t attr;
cchar_t ch;
short pair;
wchar_t wch[10];
if (getbkgrnd(&ch) != ERR) {
if (getcchar(&ch, wch, &attr, &pair, 0) != ERR) {
result = attr;
}
}
return result;
}
static int
wide_show_attr(int row, int skip, bool arrow, chtype attr, short pair, const char *name)
{
int ncv = get_ncv();
chtype test = attr & ~WA_ALTCHARSET;
if (arrow)
mvprintw(row, 5, "-->");
mvprintw(row, 8, "%s mode:", name);
mvprintw(row, 24, "|");
if (skip)
printw("%*s", skip, " ");
/*
* Just for testing, write text using the alternate character set one
* character at a time (to pass its rendition directly), and use the
* string operation for the other attributes.
*/
if (attr & WA_ALTCHARSET) {
const wchar_t *s;
cchar_t ch;
for (s = wide_attr_test_string; *s != L'\0'; ++s) {
wchar_t fill[2];
fill[0] = *s;
fill[1] = L'\0';
setcchar(&ch, fill, attr, pair, 0);
add_wch(&ch);
}
} else {
attr_t old_attr;
short old_pair;
attr_get(&old_attr, &old_pair, 0);
attr_set(attr, pair, 0);
addwstr(wide_attr_test_string);
attr_set(old_attr, old_pair, 0);
}
if (skip)
printw("%*s", skip, " ");
printw("|");
if (test != A_NORMAL) {
if (!(term_attrs() & test)) {
printw(" (N/A)");
} else {
if (ncv > 0 && (get_wide_background() & A_COLOR)) {
static const attr_t table[] =
{
WA_STANDOUT,
WA_UNDERLINE,
WA_REVERSE,
WA_BLINK,
WA_DIM,
WA_BOLD,
WA_INVIS,
WA_PROTECT,
WA_ALTCHARSET
};
unsigned n;
bool found = FALSE;
for (n = 0; n < SIZEOF(table); n++) {
if ((table[n] & attr) != 0
&& ((1 << n) & ncv) != 0) {
found = TRUE;
break;
}
}
if (found)
printw(" (NCV)");
}
if ((term_attrs() & test) != test)
printw(" (Part)");
}
}
return row + 2;
}
static bool
wide_attr_getc(int *skip, short *fg, short *bg, short *tx, int *ac, unsigned *kc)
{
bool result = TRUE;
bool error = FALSE;
WINDOW *helpwin;
do {
int ch = Getchar();
error = FALSE;
if (ch < 256 && isdigit(ch)) {
*skip = (ch - '0');
} else {
switch (ch) {
case CTRL('L'):
Repaint();
break;
case '?':
if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
box_set(helpwin, 0, 0);
attr_legend(helpwin);
wGetchar(helpwin);
delwin(helpwin);
}
break;
case 'a':
*ac = 0;
break;
case 'A':
*ac = A_ALTCHARSET;
break;
case 'v':
if (*kc == 0)
*kc = SIZEOF(attrs_to_test) - 1;
else
*kc -= 1;
break;
case 'V':
*kc += 1;
if (*kc >= SIZEOF(attrs_to_test))
*kc = 0;
break;
case '<':
wide_adjust_attr_string(-1);
break;
case '>':
wide_adjust_attr_string(1);
break;
case case_QUIT:
result = FALSE;
break;
default:
error = cycle_color_attr(ch, fg, bg, tx);
break;
}
}
} while (error);
return result;
}
static void
wide_attr_test(void)
/* test text attributes using wide-character calls */
{
int n;
int skip = get_xmc();
short fg = COLOR_BLACK; /* color pair 0 is special */
short bg = COLOR_BLACK;
short tx = -1;
int ac = 0;
unsigned j, k;
if (skip < 0)
skip = 0;
n = skip; /* make it easy */
k = SIZEOF(attrs_to_test) - 1;
wide_init_attr_string();
do {
int row = 2;
short pair = 0;
short extras = 0;
if (use_colors) {
pair = (short) (fg != COLOR_BLACK || bg != COLOR_BLACK);
if (pair != 0) {
pair = 1;
if (init_pair(pair, fg, bg) == ERR) {
beep();
}
}
extras = pair;
if (tx >= 0) {
extras = 2;
if (init_pair(extras, tx, bg) == ERR) {
beep();
}
}
}
set_wide_background(pair);
erase();
box_set(stdscr, 0, 0);
mvaddstr(0, 20, "Character attribute test display");
for (j = 0; j < SIZEOF(attrs_to_test); ++j) {
row = wide_show_attr(row, n, j == k,
ac |
attrs_to_test[j].attr |
attrs_to_test[k].attr,
extras,
attrs_to_test[j].name);
}
mvprintw(row, 8,
"This terminal does %shave the magic-cookie glitch",
get_xmc() > -1 ? "" : "not ");
mvprintw(row + 1, 8, "Enter '?' for help.");
show_color_attr(fg, bg, tx);
printw(" ACS (%d)", ac != 0);
refresh();
} while (wide_attr_getc(&n, &fg, &bg, &tx, &ac, &k));
set_wide_background(0);
erase();
endwin();
}
#endif
/****************************************************************************
*
* Color support tests
*
****************************************************************************/
static NCURSES_CONST char *the_color_names[] =
{
"black",
"red",
"green",
"yellow",
"blue",
"magenta",
"cyan",
"white",
"BLACK",
"RED",
"GREEN",
"YELLOW",
"BLUE",
"MAGENTA",
"CYAN",
"WHITE"
};
static void
show_color_name(int y, int x, int color, bool wide)
{
if (move(y, x) != ERR) {
char temp[80];
int width = 8;
if (wide) {
sprintf(temp, "%02d", color);
width = 4;
} else if (color >= 8) {
sprintf(temp, "[%02d]", color);
} else {
strcpy(temp, the_color_names[color]);
}
printw("%-*.*s", width, width, temp);
}
}
static void
color_legend(WINDOW *helpwin, bool wide)
{
int row = 1;
int col = 1;
mvwprintw(helpwin, row++, col,
"ESC to exit.");
++row;
mvwprintw(helpwin, row++, col,
"Use up/down arrow to scroll through the display if it is");
mvwprintw(helpwin, row++, col,
"longer than one screen. Control/N and Control/P can be used");
mvwprintw(helpwin, row++, col,
"in place of up/down arrow. Use pageup/pagedown to scroll a");
mvwprintw(helpwin, row++, col,
"full screen; control/B and control/F can be used here.");
++row;
mvwprintw(helpwin, row++, col,
"Toggles:");
mvwprintw(helpwin, row++, col,
" a/A toggle altcharset off/on");
mvwprintw(helpwin, row++, col,
" b/B toggle bold off/on");
mvwprintw(helpwin, row++, col,
" n/N toggle text/number on/off");
mvwprintw(helpwin, row++, col,
" w/W toggle width between 8/16 colors");
#if USE_WIDEC_SUPPORT
if (wide) {
mvwprintw(helpwin, row++, col,
"Wide characters:");
mvwprintw(helpwin, row++, col,
" x/X toggle text between ASCII and wide-character");
}
#else
(void) wide;
#endif
}
#define set_color_test(name, value) if (name != value) { name = value; base_row = 0; }
/* generate a color test pattern */
static void
color_test(void)
{
short i;
int top = 0, width;
int base_row = 0;
int grid_top = top + 3;
int page_size = (LINES - grid_top);
int pairs_max = PAIR_NUMBER(A_COLOR) + 1;
int row_limit;
int per_row;
char numbered[80];
const char *hello;
bool done = FALSE;
bool opt_acsc = FALSE;
bool opt_bold = FALSE;
bool opt_wide = FALSE;
bool opt_nums = FALSE;
WINDOW *helpwin;
if (pairs_max > COLOR_PAIRS)
pairs_max = COLOR_PAIRS;
while (!done) {
int shown = 0;
/* this assumes an 80-column line */
if (opt_wide) {
width = 4;
hello = "Test";
per_row = (COLORS > 8) ? 16 : 8;
} else {
width = 8;
hello = "Hello";
per_row = 8;
}
row_limit = (pairs_max + per_row - 1) / per_row;
move(0, 0);
(void) printw("There are %d color pairs and %d colors\n",
pairs_max, COLORS);
clrtobot();
(void) mvprintw(top + 1, 0,
"%dx%d matrix of foreground/background colors, bold *%s*\n",
row_limit,
per_row,
opt_bold ? "on" : "off");
/* show color names/numbers across the top */
for (i = 0; i < per_row; i++)
show_color_name(top + 2, (i + 1) * width, i, opt_wide);
/* show a grid of colors, with color names/ numbers on the left */
for (i = (short) (base_row * per_row); i < pairs_max; i++) {
int row = grid_top + (i / per_row) - base_row;
int col = (i % per_row + 1) * width;
short pair = i;
if (row >= 0 && move(row, col) != ERR) {
short fg = (short) (i % COLORS);
short bg = (short) (i / COLORS);
init_pair(pair, fg, bg);
attron((attr_t) COLOR_PAIR(pair));
if (opt_acsc)
attron((attr_t) A_ALTCHARSET);
if (opt_bold)
attron((attr_t) A_BOLD);
if (opt_nums) {
sprintf(numbered, "{%02X}", i);
hello = numbered;
}
printw("%-*.*s", width, width, hello);
attrset(A_NORMAL);
if ((i % per_row) == 0 && (i % COLORS) == 0) {
show_color_name(row, 0, i / COLORS, opt_wide);
}
++shown;
} else if (shown) {
break;
}
}
switch (wGetchar(stdscr)) {
case 'a':
opt_acsc = FALSE;
break;
case 'A':
opt_acsc = TRUE;
break;
case 'b':
opt_bold = FALSE;
break;
case 'B':
opt_bold = TRUE;
break;
case 'n':
opt_nums = FALSE;
break;
case 'N':
opt_nums = TRUE;
break;
case case_QUIT:
done = TRUE;
continue;
case 'w':
set_color_test(opt_wide, FALSE);
break;
case 'W':
set_color_test(opt_wide, TRUE);
break;
case CTRL('p'):
case KEY_UP:
if (base_row <= 0) {
beep();
} else {
base_row -= 1;
}
break;
case CTRL('n'):
case KEY_DOWN:
if (base_row + page_size >= row_limit) {
beep();
} else {
base_row += 1;
}
break;
case CTRL('b'):
case KEY_PREVIOUS:
case KEY_PPAGE:
if (base_row <= 0) {
beep();
} else {
base_row -= (page_size - 1);
if (base_row < 0)
base_row = 0;
}
break;
case CTRL('f'):
case KEY_NEXT:
case KEY_NPAGE:
if (base_row + page_size >= row_limit) {
beep();
} else {
base_row += page_size - 1;
if (base_row + page_size >= row_limit) {
base_row = row_limit - page_size - 1;
}
}
break;
case '?':
if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
box(helpwin, 0, 0);
color_legend(helpwin, FALSE);
wGetchar(helpwin);
delwin(helpwin);
}
break;
default:
beep();
continue;
}
}
erase();
endwin();
}
#if USE_WIDEC_SUPPORT
/* generate a color test pattern */
static void
wide_color_test(void)
{
int c;
int i;
int top = 0, width;
int base_row = 0;
int grid_top = top + 3;
int page_size = (LINES - grid_top);
int pairs_max = COLOR_PAIRS;
int row_limit;
int per_row;
char numbered[80];
const char *hello;
bool done = FALSE;
bool opt_acsc = FALSE;
bool opt_bold = FALSE;
bool opt_wide = FALSE;
bool opt_nums = FALSE;
bool opt_xchr = FALSE;
wchar_t buffer[10];
WINDOW *helpwin;
while (!done) {
int shown = 0;
/* this assumes an 80-column line */
if (opt_wide) {
width = 4;
hello = "Test";
per_row = (COLORS > 8) ? 16 : 8;
} else {
width = 8;
hello = "Hello";
per_row = 8;
}
if (opt_xchr) {
make_fullwidth_text(buffer, hello);
width *= 2;
per_row /= 2;
} else {
make_narrow_text(buffer, hello);
}
row_limit = (pairs_max + per_row - 1) / per_row;
move(0, 0);
(void) printw("There are %d color pairs and %d colors\n",
pairs_max, COLORS);
clrtobot();
(void) mvprintw(top + 1, 0,
"%dx%d matrix of foreground/background colors, bold *%s*\n",
row_limit,
per_row,
opt_bold ? "on" : "off");
/* show color names/numbers across the top */
for (i = 0; i < per_row; i++)
show_color_name(top + 2, (i + 1) * width, i, opt_wide);
/* show a grid of colors, with color names/ numbers on the left */
for (i = (base_row * per_row); i < pairs_max; i++) {
int row = grid_top + (i / per_row) - base_row;
int col = (i % per_row + 1) * width;
short pair = (short) i;
if (row >= 0 && move(row, col) != ERR) {
init_pair(pair, (short) (i % COLORS), (short) (i / COLORS));
color_set(pair, NULL);
if (opt_acsc)
attr_on((attr_t) A_ALTCHARSET, NULL);
if (opt_bold)
attr_on((attr_t) A_BOLD, NULL);
if (opt_nums) {
sprintf(numbered, "{%02X}", i);
if (opt_xchr) {
make_fullwidth_text(buffer, numbered);
} else {
make_narrow_text(buffer, numbered);
}
}
addnwstr(buffer, width);
attr_set(A_NORMAL, 0, NULL);
if ((i % per_row) == 0 && (i % COLORS) == 0) {
show_color_name(row, 0, i / COLORS, opt_wide);
}
++shown;
} else if (shown) {
break;
}
}
switch (c = wGetchar(stdscr)) {
case 'a':
opt_acsc = FALSE;
break;
case 'A':
opt_acsc = TRUE;
break;
case 'b':
opt_bold = FALSE;
break;
case 'B':
opt_bold = TRUE;
break;
case 'n':
opt_nums = FALSE;
break;
case 'N':
opt_nums = TRUE;
break;
case case_QUIT:
done = TRUE;
continue;
case 'w':
set_color_test(opt_wide, FALSE);
break;
case 'W':
set_color_test(opt_wide, TRUE);
break;
case 'x':
opt_xchr = FALSE;
break;
case 'X':
opt_xchr = TRUE;
break;
case CTRL('p'):
case KEY_UP:
if (base_row <= 0) {
beep();
} else {
base_row -= 1;
}
break;
case CTRL('n'):
case KEY_DOWN:
if (base_row + page_size >= row_limit) {
beep();
} else {
base_row += 1;
}
break;
case CTRL('b'):
case KEY_PREVIOUS:
case KEY_PPAGE:
if (base_row <= 0) {
beep();
} else {
base_row -= (page_size - 1);
if (base_row < 0)
base_row = 0;
}
break;
case CTRL('f'):
case KEY_NEXT:
case KEY_NPAGE:
if (base_row + page_size >= row_limit) {
beep();
} else {
base_row += page_size - 1;
if (base_row + page_size >= row_limit) {
base_row = row_limit - page_size - 1;
}
}
break;
case '?':
if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
box(helpwin, 0, 0);
color_legend(helpwin, TRUE);
wGetchar(helpwin);
delwin(helpwin);
}
break;
default:
beep();
continue;
}
}
erase();
endwin();
}
#endif /* USE_WIDEC_SUPPORT */
static void
change_color(short current, int field, int value, int usebase)
{
short red, green, blue;
color_content(current, &red, &green, &blue);
switch (field) {
case 0:
red = (short) (usebase ? (red + value) : value);
break;
case 1:
green = (short) (usebase ? (green + value) : value);
break;
case 2:
blue = (short) (usebase ? (blue + value) : value);
break;
}
if (init_color(current, red, green, blue) == ERR)
beep();
}
static void
init_all_colors(void)
{
short c;
for (c = 0; c < COLORS; ++c)
init_color(c,
all_colors[c].red,
all_colors[c].green,
all_colors[c].blue);
}
#define scaled_rgb(n) ((255 * (n)) / 1000)
static void
color_edit(void)
/* display the color test pattern, without trying to edit colors */
{
int i;
int current = 0;
int this_c = 0, value = 0, field = 0;
int last_c;
int top_color = 0;
int page_size = (LINES - 6);
init_all_colors();
refresh();
for (i = 0; i < max_colors; i++)
init_pair((short) i, (short) COLOR_WHITE, (short) i);
mvprintw(LINES - 2, 0, "Number: %d", value);
do {
short red, green, blue;
attron(A_BOLD);
mvaddstr(0, 20, "Color RGB Value Editing");
attroff(A_BOLD);
for (i = (short) top_color;
(i - top_color < page_size)
&& (i < max_colors); i++) {
char numeric[80];
sprintf(numeric, "[%d]", i);
mvprintw(2 + i - top_color, 0, "%c %-8s:",
(i == current ? '>' : ' '),
(i < (int) SIZEOF(the_color_names)
? the_color_names[i] : numeric));
attrset(COLOR_PAIR(i));
addstr(" ");
attrset(A_NORMAL);
color_content((short) i, &red, &green, &blue);
addstr(" R = ");
if (current == i && field == 0)
attron(A_STANDOUT);
printw("%04d", red);
if (current == i && field == 0)
attrset(A_NORMAL);
addstr(", G = ");
if (current == i && field == 1)
attron(A_STANDOUT);
printw("%04d", green);
if (current == i && field == 1)
attrset(A_NORMAL);
addstr(", B = ");
if (current == i && field == 2)
attron(A_STANDOUT);
printw("%04d", blue);
if (current == i && field == 2)
attrset(A_NORMAL);
attrset(A_NORMAL);
printw(" ( %3d %3d %3d )",
scaled_rgb(red),
scaled_rgb(green),
scaled_rgb(blue));
}
mvaddstr(LINES - 3, 0,
"Use up/down to select a color, left/right to change fields.");
mvaddstr(LINES - 2, 0,
"Modify field by typing nnn=, nnn-, or nnn+. ? for help.");
move(2 + current - top_color, 0);
last_c = this_c;
this_c = Getchar();
if (this_c < 256 && isdigit(this_c) && !isdigit(last_c))
value = 0;
switch (this_c) {
case CTRL('b'):
case KEY_PPAGE:
if (current > 0)
current -= (page_size - 1);
else
beep();
break;
case CTRL('f'):
case KEY_NPAGE:
if (current < (max_colors - 1))
current += (page_size - 1);
else
beep();
break;
case CTRL('p'):
case KEY_UP:
current = (current == 0 ? (max_colors - 1) : current - 1);
break;
case CTRL('n'):
case KEY_DOWN:
current = (current == (max_colors - 1) ? 0 : current + 1);
break;
case KEY_RIGHT:
field = (field == 2 ? 0 : field + 1);
break;
case KEY_LEFT:
field = (field == 0 ? 2 : field - 1);
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
value = value * 10 + (this_c - '0');
break;
case '+':
change_color((short) current, field, value, 1);
break;
case '-':
change_color((short) current, field, -value, 1);
break;
case '=':
change_color((short) current, field, value, 0);
break;
case '?':
erase();
P(" RGB Value Editing Help");
P("");
P("You are in the RGB value editor. Use the arrow keys to select one of");
P("the fields in one of the RGB triples of the current colors; the one");
P("currently selected will be reverse-video highlighted.");
P("");
P("To change a field, enter the digits of the new value; they are echoed");
P("as entered. Finish by typing `='. The change will take effect instantly.");
P("To increment or decrement a value, use the same procedure, but finish");
P("with a `+' or `-'.");
P("");
P("Press 'm' to invoke the top-level menu with the current color settings.");
P("To quit, do ESC");
Pause();
erase();
break;
case 'm':
endwin();
main_menu(FALSE);
refresh();
break;
case case_QUIT:
break;
default:
beep();
break;
}
if (current < 0)
current = 0;
if (current >= max_colors)
current = max_colors - 1;
if (current < top_color)
top_color = current;
if (current - top_color >= page_size)
top_color = current - (page_size - 1);
mvprintw(LINES - 1, 0, "Number: %d", value);
clrtoeol();
} while
(!isQuit(this_c));
erase();
/*
* ncurses does not reset each color individually when calling endwin().
*/
init_all_colors();
endwin();
}
/****************************************************************************
*
* Soft-key label test
*
****************************************************************************/
#if USE_SOFTKEYS
#define SLK_HELP 17
#define SLK_WORK (SLK_HELP + 3)
static void
slk_help(void)
{
static const char *table[] =
{
"Available commands are:"
,""
,"^L -- repaint this message and activate soft keys"
,"a/d -- activate/disable soft keys"
,"c -- set centered format for labels"
,"l -- set left-justified format for labels"
,"r -- set right-justified format for labels"
,"[12345678] -- set label; labels are numbered 1 through 8"
,"e -- erase stdscr (should not erase labels)"
,"s -- test scrolling of shortened screen"
#if HAVE_SLK_COLOR
,"F/B -- cycle through foreground/background colors"
#endif
,"ESC -- return to main menu"
,""
,"Note: if activating the soft keys causes your terminal to scroll up"
,"one line, your terminal auto-scrolls when anything is written to the"
,"last screen position. The ncurses code does not yet handle this"
,"gracefully."
};
unsigned j;
move(2, 0);
for (j = 0; j < SIZEOF(table); ++j) {
P(table[j]);
}
refresh();
}
#if HAVE_SLK_COLOR
static void
call_slk_color(short fg, short bg)
{
init_pair(1, bg, fg);
slk_color(1);
mvprintw(SLK_WORK, 0, "Colors %d/%d\n", fg, bg);
clrtoeol();
refresh();
}
#endif
static void
slk_test(void)
/* exercise the soft keys */
{
int c, fmt = 1;
char buf[9];
char *s;
#if HAVE_SLK_COLOR
short fg = COLOR_BLACK;
short bg = COLOR_WHITE;
#endif
c = CTRL('l');
#if HAVE_SLK_COLOR
if (use_colors) {
call_slk_color(fg, bg);
}
#endif
do {
move(0, 0);
switch (c) {
case CTRL('l'):
erase();
attron(A_BOLD);
mvaddstr(0, 20, "Soft Key Exerciser");
attroff(A_BOLD);
slk_help();
/* fall through */
case 'a':
slk_restore();
break;
case 'e':
wclear(stdscr);
break;
case 's':
mvprintw(SLK_WORK, 0, "Press Q to stop the scrolling-test: ");
while ((c = Getchar()) != 'Q' && (c != ERR))
addch((chtype) c);
break;
case 'd':
slk_clear();
break;
case 'l':
fmt = 0;
break;
case 'c':
fmt = 1;
break;
case 'r':
fmt = 2;
break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
(void) mvaddstr(SLK_WORK, 0, "Please enter the label value: ");
strcpy(buf, "");
if ((s = slk_label(c - '0')) != 0) {
strncpy(buf, s, 8);
}
wGetstring(stdscr, buf, 8);
slk_set((c - '0'), buf, fmt);
slk_refresh();
move(SLK_WORK, 0);
clrtobot();
break;
case case_QUIT:
goto done;
#if HAVE_SLK_COLOR
case 'F':
if (use_colors) {
fg = (short) ((fg + 1) % COLORS);
call_slk_color(fg, bg);
}
break;
case 'B':
if (use_colors) {
bg = (short) ((bg + 1) % COLORS);
call_slk_color(fg, bg);
}
break;
#endif
#if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
case KEY_RESIZE:
wnoutrefresh(stdscr);
break;
#endif
default:
beep();
}
} while (!isQuit(c = Getchar()));
done:
slk_clear();
erase();
endwin();
}
#if USE_WIDEC_SUPPORT
#define SLKLEN 8
static void
wide_slk_test(void)
/* exercise the soft keys */
{
int c, fmt = 1;
wchar_t buf[SLKLEN + 1];
char *s;
short fg = COLOR_BLACK;
short bg = COLOR_WHITE;
c = CTRL('l');
if (use_colors) {
call_slk_color(fg, bg);
}
do {
move(0, 0);
switch (c) {
case CTRL('l'):
erase();
attr_on(WA_BOLD, NULL);
mvaddstr(0, 20, "Soft Key Exerciser");
attr_off(WA_BOLD, NULL);
slk_help();
/* fall through */
case 'a':
slk_restore();
break;
case 'e':
wclear(stdscr);
break;
case 's':
mvprintw(SLK_WORK, 0, "Press Q to stop the scrolling-test: ");
while ((c = Getchar()) != 'Q' && (c != ERR))
addch((chtype) c);
break;
case 'd':
slk_clear();
break;
case 'l':
fmt = 0;
break;
case 'c':
fmt = 1;
break;
case 'r':
fmt = 2;
break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
(void) mvaddstr(SLK_WORK, 0, "Please enter the label value: ");
*buf = 0;
if ((s = slk_label(c - '0')) != 0) {
char *temp = strdup(s);
size_t used = strlen(temp);
size_t want = SLKLEN;
size_t test;
#ifndef state_unused
mbstate_t state;
#endif
buf[0] = L'\0';
while (want > 0 && used != 0) {
const char *base = s;
reset_mbytes(state);
test = count_mbytes(base, 0, &state);
if (test == (size_t) -1) {
temp[--used] = 0;
} else if (test > want) {
temp[--used] = 0;
} else {
reset_mbytes(state);
trans_mbytes(buf, base, want, &state);
break;
}
}
free(temp);
}
wGet_wstring(stdscr, buf, SLKLEN);
slk_wset((c - '0'), buf, fmt);
slk_refresh();
move(SLK_WORK, 0);
clrtobot();
break;
case case_QUIT:
goto done;
case 'F':
if (use_colors) {
fg = (short) ((fg + 1) % COLORS);
call_slk_color(fg, bg);
}
break;
case 'B':
if (use_colors) {
bg = (short) ((bg + 1) % COLORS);
call_slk_color(fg, bg);
}
break;
#if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
case KEY_RESIZE:
wnoutrefresh(stdscr);
break;
#endif
default:
beep();
}
} while (!isQuit(c = Getchar()));
done:
slk_clear();
erase();
endwin();
}
#endif
#endif /* SLK_INIT */
/****************************************************************************
*
* Alternate character-set stuff
*
****************************************************************************/
/* *INDENT-OFF* */
static struct {
chtype attr;
const char *name;
} attrs_to_cycle[] = {
{ A_NORMAL, "normal" },
{ A_BOLD, "bold" },
{ A_REVERSE, "reverse" },
{ A_UNDERLINE, "underline" },
};
/* *INDENT-ON* */
static bool
cycle_attr(int ch, unsigned *at_code, chtype *attr)
{
bool result = TRUE;
switch (ch) {
case 'v':
if ((*at_code += 1) >= SIZEOF(attrs_to_cycle))
*at_code = 0;
break;
case 'V':
if (*at_code == 1)
*at_code = SIZEOF(attrs_to_cycle) - 1;
else
*at_code -= 1;
break;
default:
result = FALSE;
break;
}
if (result)
*attr = attrs_to_cycle[*at_code].attr;
return result;
}
static bool
cycle_colors(int ch, int *fg, int *bg, short *pair)
{
bool result = FALSE;
if (use_colors) {
result = TRUE;
switch (ch) {
case 'F':
if ((*fg -= 1) < 0)
*fg = COLORS - 1;
break;
case 'f':
if ((*fg += 1) >= COLORS)
*fg = 0;
break;
case 'B':
if ((*bg -= 1) < 0)
*bg = COLORS - 1;
break;
case 'b':
if ((*bg += 1) >= COLORS)
*bg = 0;
break;
default:
result = FALSE;
break;
}
if (result) {
*pair = (short) (*fg != COLOR_BLACK || *bg != COLOR_BLACK);
if (*pair != 0) {
*pair = 1;
if (init_pair(*pair, (short) *fg, (short) *bg) == ERR) {
result = FALSE;
}
}
}
}
return result;
}
/* ISO 6429: codes 0x80 to 0x9f may be control characters that cause the
* terminal to perform functions. The remaining codes can be graphic.
*/
static void
show_upper_chars(unsigned first, int repeat, attr_t attr, short pair)
{
bool C1 = (first == 128);
unsigned code;
unsigned last = first + 31;
int reply;
erase();
attron(A_BOLD);
mvprintw(0, 20, "Display of %s Character Codes %d to %d",
C1 ? "C1" : "GR", first, last);
attroff(A_BOLD);
refresh();
for (code = first; code <= last; code++) {
int count = repeat;
int row = 2 + ((int) (code - first) % 16);
int col = ((int) (code - first) / 16) * COLS / 2;
char tmp[80];
sprintf(tmp, "%3u (0x%x)", code, code);
mvprintw(row, col, "%*s: ", COLS / 4, tmp);
do {
if (C1)
nodelay(stdscr, TRUE);
echochar(code | attr | COLOR_PAIR(pair));
if (C1) {
/* (yes, this _is_ crude) */
while ((reply = Getchar()) != ERR) {
addch(UChar(reply));
napms(10);
}
nodelay(stdscr, FALSE);
}
} while (--count > 0);
}
}
#define PC_COLS 4
static void
show_pc_chars(int repeat, attr_t attr, short pair)
{
unsigned code;
erase();
attron(A_BOLD);
mvprintw(0, 20, "Display of PC Character Codes");
attroff(A_BOLD);
refresh();
for (code = 0; code < 16; ++code) {
mvprintw(2, (int) code * PC_COLS + 8, "%X", code);
}
for (code = 0; code < 256; code++) {
int count = repeat;
int row = 3 + (int) (code / 16) + (code >= 128);
int col = 8 + (int) (code % 16) * PC_COLS;
if ((code % 16) == 0)
mvprintw(row, 0, "0x%02x:", code);
move(row, col);
do {
switch (code) {
case '\n':
case '\r':
case '\b':
case '\f':
case '\033':
case 0x9b:
/*
* Skip the ones that do not work.
*/
break;
default:
addch(code | A_ALTCHARSET | attr | COLOR_PAIR(pair));
break;
}
} while (--count > 0);
}
}
static void
show_box_chars(int repeat, attr_t attr, short pair)
{
(void) repeat;
attr |= COLOR_PAIR(pair);
erase();
attron(A_BOLD);
mvaddstr(0, 20, "Display of the ACS Line-Drawing Set");
attroff(A_BOLD);
refresh();
box(stdscr, 0, 0);
/* *INDENT-OFF* */
mvhline(LINES / 2, 0, ACS_HLINE | attr, COLS);
mvvline(0, COLS / 2, ACS_VLINE | attr, LINES);
mvaddch(0, COLS / 2, ACS_TTEE | attr);
mvaddch(LINES / 2, COLS / 2, ACS_PLUS | attr);
mvaddch(LINES - 1, COLS / 2, ACS_BTEE | attr);
mvaddch(LINES / 2, 0, ACS_LTEE | attr);
mvaddch(LINES / 2, COLS - 1, ACS_RTEE | attr);
/* *INDENT-ON* */
}
static int
show_1_acs(int n, int repeat, const char *name, chtype code)
{
const int height = 16;
int row = 2 + (n % height);
int col = (n / height) * COLS / 2;
mvprintw(row, col, "%*s : ", COLS / 4, name);
do {
addch(code);
} while (--repeat > 0);
return n + 1;
}
static void
show_acs_chars(int repeat, attr_t attr, short pair)
/* display the ACS character set */
{
int n;
#define BOTH(name