/**************************************************************************** NAME ncurses.c --- ncurses library exerciser SYNOPSIS ncurses DESCRIPTION An interactive test module for the ncurses library. AUTHOR This software is Copyright (C) 1993 by Eric S. Raymond, all rights reserved. It is issued with ncurses under the same terms and conditions as the ncurses library source. $Id: ncurses.c,v 1.91 1997/05/10 18:19:48 tom Exp $ ***************************************************************************/ #include #include #include #include #include #include #if HAVE_GETTIMEOFDAY #if HAVE_SYS_TIME_H && ! SYSTEM_LOOKS_LIKE_SCO #include #endif #if HAVE_SYS_SELECT_H #include #endif #endif #if HAVE_PANEL_H #define USE_LIBPANEL 1 #include #else #define USE_LIBPANEL 0 #endif #if HAVE_MENU_H && HAVE_LIBMENU #define USE_LIBMENU 1 #include #else #define USE_LIBMENU 0 #endif #if HAVE_FORM_H && HAVE_LIBFORM #define USE_LIBFORM 1 #include #else #define USE_LIBFORM 0 #endif #ifdef NCURSES_VERSION #ifdef TRACE static int save_trace = TRACE_ORDINARY|TRACE_CALLS; extern int _nc_tracing; #endif #if !HAVE_NAPMS #define HAVE_NAPMS 1 #endif #else #define mmask_t chtype /* not specified in XSI */ #define attr_t chtype /* not specified in XSI */ #define ACS_S3 (acs_map['p']) /* scan line 3 */ #define ACS_S7 (acs_map['r']) /* scan line 7 */ #define ACS_LEQUAL (acs_map['y']) /* less/equal */ #define ACS_GEQUAL (acs_map['z']) /* greater/equal */ #define ACS_PI (acs_map['{']) /* Pi */ #define ACS_NEQUAL (acs_map['|']) /* not equal */ #define ACS_STERLING (acs_map['}']) /* UK pound sign */ #endif #define P(string) printw("%s\n", string) #ifndef CTRL #define CTRL(x) ((x) & 0x1f) #endif #define SIZEOF(table) (sizeof(table)/sizeof(table[0])) #define QUIT CTRL('Q') #define ESCAPE CTRL('[') #define BLANK ' ' /* this is the background character */ /* 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)); } /* 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; _tracef("TOGGLE-TRACING OFF"); _nc_tracing = 0; } else { _nc_tracing = save_trace; } trace(_nc_tracing); if (_nc_tracing) _tracef("TOGGLE-TRACING ON"); } #else c = wgetch(win); #endif return c; } #define Getchar() wGetchar(stdscr) 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(); } /**************************************************************************** * * Character input test * ****************************************************************************/ static void getch_test(void) /* test the keypad feature */ { char buf[BUFSIZ]; int c; int incount = 0, firsttime = 0; bool blocking = TRUE; int y, x; refresh(); #ifdef NCURSES_MOUSE_VERSION mousemask(ALL_MOUSE_EVENTS, (mmask_t *)0); #endif (void) printw("Delay in 10ths of a second ( for blocking input)? "); echo(); getstr(buf); noecho(); nonl(); if (isdigit(buf[0])) { timeout(atoi(buf) * 100); blocking = FALSE; } c = '?'; raw(); for (;;) { if (firsttime++) { printw("Key pressed: %04o ", c); #ifdef NCURSES_MOUSE_VERSION if (c == KEY_MOUSE) { MEVENT event; getmouse(&event); printw("KEY_MOUSE, %s\n", _tracemouse(&event)); } else #endif /* NCURSES_MOUSE_VERSION */ if (c >= KEY_MIN) { (void) addstr(keyname(c)); addch('\n'); } else if (c > 0x80) { int c2 = (c & 0x7f); if (isprint(c2)) (void) printw("M-%c", c2); else (void) printw("M-%s", unctrl(c2)); addstr(" (high-half character)\n"); } else { if (isprint(c)) (void) printw("%c (ASCII printable character)\n", c); else (void) printw("%s (ASCII control character)\n", unctrl(c)); } getyx(stdscr, y, x); if (y >= LINES-1) move(0,0); clrtoeol(); } if (c == 'g') { addstr("getstr test: "); echo(); getstr(buf); noecho(); printw("I saw `%s'.\n", buf); } if (c == 's') { ShellOut(TRUE); } if (c == 'x' || c == 'q' || (c == ERR && blocking)) break; if (c == '?') { addstr("Type any key to see its keypad value. Also:\n"); addstr("g -- triggers a getstr test\n"); addstr("s -- shell out\n"); addstr("q -- quit\n"); addstr("? -- repeats this help message\n"); } while ((c = Getchar()) == ERR) if (!blocking) (void) printw("%05d: input timed out\n", incount++); else { (void) printw("%05d: input error\n", incount++); break; } } #ifdef NCURSES_MOUSE_VERSION mousemask(0, (mmask_t *)0); #endif timeout(-1); erase(); noraw(); nl(); endwin(); } static int show_attr(int row, chtype attr, const char *name, bool once, const char *capname) { mvprintw(row, 8, "%s mode:", name); mvprintw(row, 24, "|"); if (once) attron(attr); else attrset(attr); addstr("abcde fghij klmno pqrst uvwxy z"); if (once) attroff(attr); printw("|"); if (capname != 0 && tigetstr(capname) == 0) printw(" (N/A)"); return row + 2; } static void attr_test(void) /* test text attributes */ { int row = 2; refresh(); mvaddstr(0, 20, "Character attribute test display"); row = show_attr(row, A_STANDOUT, "STANDOUT", TRUE, "smso"); row = show_attr(row, A_REVERSE, "REVERSE", TRUE, "rev"); row = show_attr(row, A_BOLD, "BOLD", TRUE, "bold"); row = show_attr(row, A_UNDERLINE, "UNDERLINE", TRUE, "smul"); row = show_attr(row, A_DIM, "DIM", TRUE, "dim"); row = show_attr(row, A_BLINK, "BLINK", TRUE, "blink"); row = show_attr(row, A_PROTECT, "PROTECT", TRUE, "prot"); row = show_attr(row, A_INVIS, "INVISIBLE", TRUE, "invis"); row = show_attr(row, A_NORMAL, "NORMAL", FALSE,0); mvprintw(row, 8, "This terminal does %shave the magic-cookie glitch", tigetnum("xmc") > -1 ? "" : "not "); refresh(); Pause(); erase(); endwin(); } /**************************************************************************** * * Color support tests * ****************************************************************************/ static NCURSES_CONST char *colors[] = { "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white" }; static void color_test(void) /* generate a color test pattern */ { int i; refresh(); (void) printw("There are %d color pairs\n", COLOR_PAIRS); (void) mvprintw(1, 0, "%dx%d matrix of foreground/background colors, bright *off*\n", COLORS, COLORS); for (i = 0; i < COLORS; i++) mvaddstr(2, (i+1) * 8, colors[i]); for (i = 0; i < COLORS; i++) mvaddstr(3 + i, 0, colors[i]); for (i = 1; i < COLOR_PAIRS; i++) { init_pair(i, i % COLORS, i / COLORS); attron((attr_t)COLOR_PAIR(i)); mvaddstr(3 + (i / COLORS), (i % COLORS + 1) * 8, "Hello"); attrset(A_NORMAL); } (void) mvprintw(COLORS + 4, 0, "%dx%d matrix of foreground/background colors, bright *on*\n", COLORS, COLORS); for (i = 0; i < COLORS; i++) mvaddstr(5 + COLORS, (i+1) * 8, colors[i]); for (i = 0; i < COLORS; i++) mvaddstr(6 + COLORS + i, 0, colors[i]); for (i = 1; i < COLOR_PAIRS; i++) { init_pair(i, i % COLORS, i / COLORS); attron((attr_t)(COLOR_PAIR(i) | A_BOLD)); mvaddstr(6 + COLORS + (i / COLORS), (i % COLORS + 1) * 8, "Hello"); attrset(A_NORMAL); } Pause(); erase(); endwin(); } static void change_color(int current, int field, int value, int usebase) { short red, green, blue; if (usebase) color_content(current, &red, &green, &blue); else red = green = blue = 0; switch (field) { case 0: red += value; break; case 1: green += value; break; case 2: blue += value; break; } if (init_color(current, red, green, blue) == ERR) beep(); } static void color_edit(void) /* display the color test pattern, without trying to edit colors */ { int i, this_c = 0, value = 0, current = 0, field = 0; int last_c; refresh(); for (i = 0; i < COLORS; i++) init_pair(i, COLOR_WHITE, 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 = 0; i < COLORS; i++) { mvprintw(2 + i, 0, "%c %-8s:", (i == current ? '>' : ' '), (i < (int) SIZEOF(colors) ? colors[i] : "")); attrset(COLOR_PAIR(i)); addstr(" "); attrset(A_NORMAL); /* * Note: this refresh should *not* be necessary! It works around * a bug in attribute handling that apparently causes the A_NORMAL * attribute sets to interfere with the actual emission of the * color setting somehow. This needs to be fixed. */ refresh(); color_content(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); addstr(")"); } mvaddstr(COLORS + 3, 0, "Use up/down to select a color, left/right to change fields."); mvaddstr(COLORS + 4, 0, "Modify field by typing nnn=, nnn-, or nnn+. ? for help."); move(2 + current, 0); last_c = this_c; this_c = Getchar(); if (isdigit(this_c) && !isdigit(last_c)) value = 0; switch (this_c) { case KEY_UP: current = (current == 0 ? (COLORS - 1) : current - 1); break; case KEY_DOWN: current = (current == (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(current, field, value, 1); break; case '-': change_color(current, field, -value, 1); break; case '=': change_color(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("echoed. 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("To quit, do `x' or 'q'"); Pause(); erase(); break; case 'x': case 'q': break; default: beep(); break; } mvprintw(LINES-2, 0, "Number: %d", value); clrtoeol(); } while (this_c != 'x' && this_c != 'q'); erase(); endwin(); } /**************************************************************************** * * Soft-key label test * ****************************************************************************/ static void slk_test(void) /* exercise the soft keys */ { int c, fmt = 1; char buf[9]; c = CTRL('l'); do { move(0, 0); switch(c) { case CTRL('l'): erase(); attron(A_BOLD); mvaddstr(0, 20, "Soft Key Exerciser"); attroff(A_BOLD); move(2, 0); P("Available commands are:"); P(""); P("^L -- refresh screen"); P("a -- activate or restore soft keys"); P("d -- disable soft keys"); P("c -- set centered format for labels"); P("l -- set left-justified format for labels"); P("r -- set right-justified format for labels"); P("[12345678] -- set label; labels are numbered 1 through 8"); P("e -- erase stdscr (should not erase labels)"); P("s -- test scrolling of shortened screen"); P("x, q -- return to main menu"); P(""); P("Note: if activating the soft keys causes your terminal to"); P("scroll up one line, your terminal auto-scrolls when anything"); P("is written to the last screen position. The ncurses code"); P("does not yet handle this gracefully."); refresh(); /* fall through */ case 'a': slk_restore(); break; case 'e': wclear(stdscr); break; case 's': mvprintw(20, 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(20, 0, "Please enter the label value: "); echo(); wgetnstr(stdscr, buf, 8); noecho(); slk_set((c - '0'), buf, fmt); slk_refresh(); move(20, 0); clrtoeol(); break; case 'x': case 'q': goto done; default: beep(); } } while ((c = Getchar()) != EOF); done: erase(); endwin(); } /**************************************************************************** * * Alternate character-set stuff * ****************************************************************************/ /* 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(int first) { bool C1 = (first == 128); int code; int 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 row = 4 + ((code - first) % 16); int col = ((code - first) / 16) * COLS / 2; char tmp[80]; sprintf(tmp, "%3d (0x%x)", code, code); mvprintw(row, col, "%*s: ", COLS/4, tmp); if (C1) nodelay(stdscr, TRUE); echochar(code); if (C1) { /* (yes, this _is_ crude) */ while ((reply = Getchar()) != ERR) { addch(reply); napms(10); } nodelay(stdscr, FALSE); } } } static int show_1_acs(int n, const char *name, chtype code) { const int height = 16; int row = 4 + (n % height); int col = (n / height) * COLS / 2; mvprintw(row, col, "%*s : ", COLS/4, name); addch(code); return n + 1; } static void show_acs_chars(void) /* display the ACS character set */ { int n; #define BOTH(name) #name, name erase(); attron(A_BOLD); mvaddstr(0, 20, "Display of the ACS Character Set"); attroff(A_BOLD); refresh(); n = show_1_acs(0, BOTH(ACS_ULCORNER)); n = show_1_acs(n, BOTH(ACS_LLCORNER)); n = show_1_acs(n, BOTH(ACS_URCORNER)); n = show_1_acs(n, BOTH(ACS_LRCORNER)); n = show_1_acs(n, BOTH(ACS_RTEE)); n = show_1_acs(n, BOTH(ACS_LTEE)); n = show_1_acs(n, BOTH(ACS_BTEE)); n = show_1_acs(n, BOTH(ACS_TTEE)); n = show_1_acs(n, BOTH(ACS_HLINE)); n = show_1_acs(n, BOTH(ACS_VLINE)); n = show_1_acs(n, BOTH(ACS_PLUS)); n = show_1_acs(n, BOTH(ACS_S1)); n = show_1_acs(n, BOTH(ACS_S9)); n = show_1_acs(n, BOTH(ACS_DIAMOND)); n = show_1_acs(n, BOTH(ACS_CKBOARD)); n = show_1_acs(n, BOTH(ACS_DEGREE)); n = show_1_acs(n, BOTH(ACS_PLMINUS)); n = show_1_acs(n, BOTH(ACS_BULLET)); n = show_1_acs(n, BOTH(ACS_LARROW)); n = show_1_acs(n, BOTH(ACS_RARROW)); n = show_1_acs(n, BOTH(ACS_DARROW)); n = show_1_acs(n, BOTH(ACS_UARROW)); n = show_1_acs(n, BOTH(ACS_BOARD)); n = show_1_acs(n, BOTH(ACS_LANTERN)); n = show_1_acs(n, BOTH(ACS_BLOCK)); n = show_1_acs(n, BOTH(ACS_S3)); n = show_1_acs(n, BOTH(ACS_S7)); n = show_1_acs(n, BOTH(ACS_LEQUAL)); n = show_1_acs(n, BOTH(ACS_GEQUAL)); n = show_1_acs(n, BOTH(ACS_PI)); n = show_1_acs(n, BOTH(ACS_NEQUAL)); n = show_1_acs(n, BOTH(ACS_STERLING)); } static void acs_display(void) { int c = 'a'; do { switch (c) { case 'a': show_acs_chars(); break; case '0': case '1': case '2': case '3': show_upper_chars((c - '0') * 32 + 128); break; } mvprintw(LINES-3,0, "Note: ANSI terminals may not display C1 characters."); mvprintw(LINES-2,0, "Select: a=ACS, 0=C1, 1,2,3=GR characters, q=quit"); refresh(); } while ((c = Getchar()) != 'x' && c != 'q'); Pause(); erase(); endwin(); } /* * Graphic-rendition test (adapted from vttest) */ static void test_sgr_attributes(void) { int pass; for (pass = 0; pass < 2; pass++) { int normal = ((pass == 0 ? A_NORMAL : A_REVERSE)) | BLANK; /* Use non-default colors if possible to exercise bce a little */ if (has_colors()) { init_pair(1, COLOR_WHITE, COLOR_BLUE); normal |= COLOR_PAIR(1); } bkgdset(normal); erase(); mvprintw( 1,20, "Graphic rendition test pattern:"); mvprintw( 4, 1, "vanilla"); #define set_sgr(mask) bkgdset((normal^(mask))); set_sgr(A_BOLD); mvprintw( 4,40, "bold"); set_sgr(A_UNDERLINE); mvprintw( 6, 6, "underline"); set_sgr(A_BOLD|A_UNDERLINE); mvprintw( 6,45, "bold underline"); set_sgr(A_BLINK); mvprintw( 8, 1, "blink"); set_sgr(A_BLINK|A_BOLD); mvprintw( 8,40, "bold blink"); set_sgr(A_UNDERLINE|A_BLINK); mvprintw(10, 6, "underline blink"); set_sgr(A_BOLD|A_UNDERLINE|A_BLINK); mvprintw(10,45, "bold underline blink"); set_sgr(A_REVERSE); mvprintw(12, 1, "negative"); set_sgr(A_BOLD|A_REVERSE); mvprintw(12,40, "bold negative"); set_sgr(A_UNDERLINE|A_REVERSE); mvprintw(14, 6, "underline negative"); set_sgr(A_BOLD|A_UNDERLINE|A_REVERSE); mvprintw(14,45, "bold underline negative"); set_sgr(A_BLINK|A_REVERSE); mvprintw(16, 1, "blink negative"); set_sgr(A_BOLD|A_BLINK|A_REVERSE); mvprintw(16,40, "bold blink negative"); set_sgr(A_UNDERLINE|A_BLINK|A_REVERSE); mvprintw(18, 6, "underline blink negative"); set_sgr(A_BOLD|A_UNDERLINE|A_BLINK|A_REVERSE); mvprintw(18,45, "bold underline blink negative"); bkgdset(normal); mvprintw(LINES-2,1, "%s background. ", pass == 0 ? "Dark" : "Light"); clrtoeol(); Pause(); } bkgdset(A_NORMAL | BLANK); erase(); endwin(); } /**************************************************************************** * * Windows and scrolling tester. * ****************************************************************************/ #define BOTLINES 4 /* number of line stolen from screen bottom */ typedef struct { int y, x; } pair; #define FRAME struct frame FRAME { FRAME *next, *last; bool do_scroll; bool do_keypad; WINDOW *wind; }; /* We need to know if these flags are actually set, so don't look in FRAME. * These names are known to work with SVr4 curses as well as ncurses. */ static bool HaveKeypad(FRAME *curp) { WINDOW *win = (curp ? curp->wind : stdscr); return win->_use_keypad; } static bool HaveScroll(FRAME *curp) { WINDOW *win = (curp ? curp->wind : stdscr); return win->_scroll; } static void newwin_legend(FRAME *curp) { static const struct { const char *msg; int code; } legend[] = { { "^C = create window", 0 }, { "^N = next window", 0 }, { "^P = previous window", 0 }, { "^F = scroll forward", 0 }, { "^B = scroll backward", 0 }, { "^K = keypad(%s)", 1 }, { "^S = scrollok(%s)", 2 }, { "^W = save window to file", 0 }, { "^R = restore window", 0 }, #ifdef NCURSES_VERSION { "^X = resize", 0 }, #endif { "^Q%s = exit", 3 } }; size_t n; int y, x; bool do_keypad = HaveKeypad(curp); bool do_scroll = HaveScroll(curp); char buf[BUFSIZ]; move(LINES-4, 0); for (n = 0; n < SIZEOF(legend); n++) { switch (legend[n].code) { default: strcpy(buf, legend[n].msg); break; case 1: sprintf(buf, legend[n].msg, do_keypad ? "yes" : "no"); break; case 2: sprintf(buf, legend[n].msg, do_scroll ? "yes" : "no"); break; case 3: sprintf(buf, legend[n].msg, do_keypad ? "/ESC" : ""); break; } getyx(stdscr, y, x); addstr((COLS < (x + 3 + (int)strlen(buf))) ? "\n" : (n ? ", " : "")); addstr(buf); } clrtoeol(); } static void transient(FRAME *curp, NCURSES_CONST char *msg) { newwin_legend(curp); if (msg) { mvaddstr(LINES - 1, 0, msg); refresh(); napms(1000); } move(LINES-1, 0); printw("%s characters are echoed, window should %sscroll.", HaveKeypad(curp) ? "Non-arrow" : "All other", HaveScroll(curp) ? "" : "not " ); clrtoeol(); } static void newwin_report(FRAME *curp) /* report on the cursor's current position, then restore it */ { WINDOW *win = (curp != 0) ? curp->wind : stdscr; int y, x; if (win != stdscr) transient(curp, (char *)0); getyx(win, y, x); move(LINES - 1, COLS - 17); printw("Y = %2d X = %2d", y, x); if (win != stdscr) refresh(); else wmove(win, y, x); } static pair *selectcell(int uli, int ulj, int lri, int lrj) /* arrows keys move cursor, return location at current on non-arrow key */ { static pair res; /* result cell */ int si = lri - uli + 1; /* depth of the select area */ int sj = lrj - ulj + 1; /* width of the select area */ int i = 0, j = 0; /* offsets into the select area */ res.y = uli; res.x = ulj; for (;;) { move(uli + i, ulj + j); newwin_report((FRAME *)0); switch(Getchar()) { case KEY_UP: i += si - 1; break; case KEY_DOWN: i++; break; case KEY_LEFT: j += sj - 1; break; case KEY_RIGHT: j++; break; case QUIT: case ESCAPE: return((pair *)0); #ifdef NCURSES_MOUSE_VERSION case KEY_MOUSE: { MEVENT event; getmouse(&event); if (event.y > uli && event.x > ulj) { i = event.y - uli; j = event.x - ulj; } else { beep(); break; } } /* FALLTHRU */ #endif default: res.y = uli + i; res.x = ulj + j; return(&res); } i %= si; j %= sj; } } static void outerbox(pair ul, pair lr, bool onoff) /* draw or erase a box *outside* the given pair of corners */ { mvaddch(ul.y-1, lr.x-1, onoff ? ACS_ULCORNER : ' '); mvaddch(ul.y-1, lr.x+1, onoff ? ACS_URCORNER : ' '); mvaddch(lr.y+1, lr.x+1, onoff ? ACS_LRCORNER : ' '); mvaddch(lr.y+1, ul.x-1, onoff ? ACS_LLCORNER : ' '); move(ul.y-1, ul.x); hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1); move(ul.y, ul.x-1); vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1); move(lr.y+1, ul.x); hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1); move(ul.y, lr.x+1); vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1); } static WINDOW *getwindow(void) /* Ask user for a window definition */ { WINDOW *rwindow; pair ul, lr, *tmp; move(0, 0); clrtoeol(); addstr("Use arrows to move cursor, anything else to mark corner 1"); refresh(); if ((tmp = selectcell(2, 1, LINES-BOTLINES-2, COLS-2)) == (pair *)0) return((WINDOW *)0); memcpy(&ul, tmp, sizeof(pair)); mvaddch(ul.y-1, ul.x-1, ACS_ULCORNER); move(0, 0); clrtoeol(); addstr("Use arrows to move cursor, anything else to mark corner 2"); refresh(); if ((tmp = selectcell(ul.y, ul.x, LINES-BOTLINES-2, COLS-2)) == (pair *)0) return((WINDOW *)0); memcpy(&lr, tmp, sizeof(pair)); rwindow = subwin(stdscr, lr.y - ul.y + 1, lr.x - ul.x + 1, ul.y, ul.x); outerbox(ul, lr, TRUE); refresh(); wrefresh(rwindow); move(0, 0); clrtoeol(); return(rwindow); } static void newwin_move(FRAME *curp, int dy, int dx) { WINDOW *win = (curp != 0) ? curp->wind : stdscr; int cur_y, cur_x; int max_y, max_x; getyx(win, cur_y, cur_x); getmaxyx(win, max_y, max_x); if ((cur_x += dx) < 0) cur_x = 0; else if (cur_x >= max_x) cur_x = max_x - 1; if ((cur_y += dy) < 0) cur_y = 0; else if (cur_y >= max_y) cur_y = max_y - 1; wmove(win, cur_y, cur_x); } static FRAME *delete_framed(FRAME *fp, bool showit) { FRAME *np; fp->last->next = fp->next; fp->next->last = fp->last; if (showit) { werase(fp->wind); wrefresh(fp->wind); } delwin(fp->wind); np = (fp == fp->next) ? 0 : fp->next; free(fp); return np; } static void acs_and_scroll(void) /* Demonstrate windows */ { int c, i; FILE *fp; FRAME *current = (FRAME *)0, *neww; WINDOW *usescr = stdscr; #define DUMPFILE "screendump" #ifdef NCURSES_MOUSE_VERSION mousemask(BUTTON1_CLICKED, (mmask_t *)0); #endif c = CTRL('C'); raw(); do { transient((FRAME *)0, (char *)0); switch(c) { case CTRL('C'): neww = (FRAME *) calloc(1, sizeof(FRAME)); if ((neww->wind = getwindow()) == (WINDOW *)0) goto breakout; if (current == 0) /* First element, */ { neww->next = neww; /* so point it at itself */ neww->last = neww; } else { neww->next = current->next; neww->last = current; neww->last->next = neww; neww->next->last = neww; } current = neww; current->do_keypad = HaveKeypad(current); current->do_scroll = HaveScroll(current); break; case CTRL('N'): /* go to next window */ if (current) current = current->next; break; case CTRL('P'): /* go to previous window */ if (current) current = current->last; break; case CTRL('F'): /* scroll current window forward */ if (current) wscrl(current->wind, 1); break; case CTRL('B'): /* scroll current window backwards */ if (current) wscrl(current->wind, -1); break; case CTRL('K'): /* toggle keypad mode for current */ if (current) { current->do_keypad = !current->do_keypad; keypad(current->wind, current->do_keypad); } break; case CTRL('S'): if (current) { current->do_scroll = !current->do_scroll; scrollok(current->wind, current->do_scroll); } break; case CTRL('W'): /* save and delete window */ if (current == current->next) break; if ((fp = fopen(DUMPFILE, "w")) == (FILE *)0) transient(current, "Can't open screen dump file"); else { (void) putwin(current->wind, fp); (void) fclose(fp); current = delete_framed(current, TRUE); } break; case CTRL('R'): /* restore window */ if ((fp = fopen(DUMPFILE, "r")) == (FILE *)0) transient(current, "Can't open screen dump file"); else { neww = (FRAME *) calloc(1, sizeof(FRAME)); neww->next = current->next; neww->last = current; neww->last->next = neww; neww->next->last = neww; neww->wind = getwin(fp); (void) fclose(fp); wrefresh(neww->wind); } break; #ifdef NCURSES_VERSION case CTRL('X'): /* resize window */ if (current) { pair *tmp, ul, lr; move(0, 0); clrtoeol(); addstr("Use arrows to move cursor, anything else to mark new corner"); refresh(); getbegyx(current->wind, ul.y, ul.x); tmp = selectcell(ul.y, ul.x, LINES-BOTLINES-2, COLS-2); if (tmp == (pair *)0) { beep(); break; } getmaxyx(current->wind, lr.y, lr.x); lr.y += (ul.y - 1); lr.x += (ul.x - 1); outerbox(ul, lr, FALSE); wnoutrefresh(stdscr); /* strictly cosmetic hack for the test */ if (current->wind->_maxy > tmp->y - ul.y) { getyx(current->wind, lr.y, lr.x); wmove(current->wind, tmp->y - ul.y + 1, 0); wclrtobot(current->wind); wmove(current->wind, lr.y, lr.x); } if (current->wind->_maxx > tmp->x - ul.x) for (i = 0; i < current->wind->_maxy; i++) { wmove(current->wind, i, tmp->x - ul.x + 1); wclrtoeol(current->wind); } wnoutrefresh(current->wind); memcpy(&lr, tmp, sizeof(pair)); (void) wresize(current->wind, lr.y-ul.y+0, lr.x-ul.x+0); getbegyx(current->wind, ul.y, ul.x); getmaxyx(current->wind, lr.y, lr.x); lr.y += (ul.y - 1); lr.x += (ul.x - 1); outerbox(ul, lr, TRUE); wnoutrefresh(stdscr); wnoutrefresh(current->wind); move(0, 0); clrtoeol(); doupdate(); } break; #endif /* NCURSES_VERSION */ case KEY_F(10): /* undocumented --- use this to test area clears */ selectcell(0, 0, LINES - 1, COLS - 1); clrtobot(); refresh(); break; case KEY_UP: newwin_move(current, -1, 0); break; case KEY_DOWN: newwin_move(current, 1, 0); break; case KEY_LEFT: newwin_move(current, 0, -1); break; case KEY_RIGHT: newwin_move(current, 0, 1); break; case KEY_BACKSPACE: /* FALLTHROUGH */ case KEY_DC: { int y, x; getyx(current->wind, y, x); if (--x < 0) { if (--y < 0) break; x = getmaxx(current->wind) - 1; } mvwdelch(current->wind, y, x); } break; case '\r': c = '\n'; /* FALLTHROUGH */ default: if (current) waddch(current->wind, (chtype)c); else beep(); break; } newwin_report(current); usescr = (current ? current->wind : stdscr); wrefresh(usescr); } while ((c = wGetchar(usescr)) && !((c == ESCAPE) && (usescr->_use_keypad)) && (c != ERR)); breakout: while (current != 0) current = delete_framed(current, FALSE); scrollok(stdscr, TRUE); /* reset to driver's default */ #ifdef NCURSES_MOUSE_VERSION mousemask(0, (mmask_t *)0); #endif noraw(); erase(); endwin(); } /**************************************************************************** * * Panels tester * ****************************************************************************/ #if USE_LIBPANEL static PANEL *p1; static PANEL *p2; static PANEL *p3; static PANEL *p4; static PANEL *p5; static WINDOW *w1; static WINDOW *w2; static WINDOW *w3; static WINDOW *w4; static WINDOW *w5; static unsigned long nap_msec = 1; static NCURSES_CONST char *mod[] = { "test ", "TEST ", "(**) ", "*()* ", "<--> ", "LAST " }; /*+------------------------------------------------------------------------- wait_a_while(msec) --------------------------------------------------------------------------*/ static void wait_a_while(unsigned long msec GCC_UNUSED) { #if HAVE_NAPMS if(nap_msec == 1) wGetchar(stdscr); else napms(nap_msec); #else if(nap_msec == 1) wGetchar(stdscr); else if(msec > 1000L) sleep((int)msec/1000L); else sleep(1); #endif } /* end of wait_a_while */ /*+------------------------------------------------------------------------- saywhat(text) --------------------------------------------------------------------------*/ static void saywhat(NCURSES_CONST char *text) { wmove(stdscr,LINES - 1,0); wclrtoeol(stdscr); waddstr(stdscr, text); } /* end of saywhat */ /*+------------------------------------------------------------------------- mkpanel(rows,cols,tly,tlx) - alloc a win and panel and associate them --------------------------------------------------------------------------*/ static PANEL * mkpanel(int rows, int cols, int tly, int tlx) { WINDOW *win = newwin(rows,cols,tly,tlx); PANEL *pan; if(!win) return((PANEL *)0); if((pan = new_panel(win))) return(pan); delwin(win); return((PANEL *)0); } /* end of mkpanel */ /*+------------------------------------------------------------------------- rmpanel(pan) --------------------------------------------------------------------------*/ static void rmpanel(PANEL *pan) { WINDOW *win = panel_window(pan); del_panel(pan); delwin(win); } /* end of rmpanel */ /*+------------------------------------------------------------------------- pflush() --------------------------------------------------------------------------*/ static void pflush(void) { update_panels(); doupdate(); } /* end of pflush */ /*+------------------------------------------------------------------------- fill_panel(win) --------------------------------------------------------------------------*/ static void fill_panel(PANEL *pan) { WINDOW *win = panel_window(pan); int num = ((const char *)panel_userptr(pan))[1]; int y,x; box(win, 0, 0); wmove(win,1,1); wprintw(win,"-pan%c-", num); for(y = 2; y < getmaxy(win) - 1; y++) { for(x = 1; x < getmaxx(win) - 1; x++) { wmove(win,y,x); waddch(win,num); } } } /* end of fill_panel */ static void demo_panels(void) { int itmp; register y,x; refresh(); for(y = 0; y < LINES - 1; y++) { for(x = 0; x < COLS; x++) wprintw(stdscr,"%d",(y + x) % 10); } for(y = 0; y < 5; y++) { p1 = mkpanel(LINES/2 - 2, COLS/8 + 1, 0, 0); w1 = panel_window(p1); set_panel_userptr(p1,"p1"); p2 = mkpanel(LINES/2 + 1, COLS/7, LINES/4, COLS/10); w2 = panel_window(p2); set_panel_userptr(p2,"p2"); p3 = mkpanel(LINES/4, COLS/10, LINES/2, COLS/9); w3 = panel_window(p3); set_panel_userptr(p3,"p3"); p4 = mkpanel(LINES/2 - 2, COLS/8, LINES/2 - 2, COLS/3); w4 = panel_window(p4); set_panel_userptr(p4,"p4"); p5 = mkpanel(LINES/2 - 2, COLS/8, LINES/2, COLS/2 - 2); w5 = panel_window(p5); set_panel_userptr(p5,"p5"); fill_panel(p1); fill_panel(p2); fill_panel(p3); fill_panel(p4); fill_panel(p5); hide_panel(p4); hide_panel(p5); pflush(); saywhat("press any key to continue"); wait_a_while(nap_msec); saywhat("h3 s1 s2 s4 s5; press any key to continue"); move_panel(p1,0,0); hide_panel(p3); show_panel(p1); show_panel(p2); show_panel(p4); show_panel(p5); pflush(); wait_a_while(nap_msec); saywhat("s1; press any key to continue"); show_panel(p1); pflush(); wait_a_while(nap_msec); saywhat("s2; press any key to continue"); show_panel(p2); pflush(); wait_a_while(nap_msec); saywhat("m2; press any key to continue"); move_panel(p2, LINES/3 + 1, COLS / 8); pflush(); wait_a_while(nap_msec); saywhat("s3;"); show_panel(p3); pflush(); wait_a_while(nap_msec); saywhat("m3; press any key to continue"); move_panel(p3, LINES/4 + 1, COLS / 15); pflush(); wait_a_while(nap_msec); saywhat("b3; press any key to continue"); bottom_panel(p3); pflush(); wait_a_while(nap_msec); saywhat("s4; press any key to continue"); show_panel(p4); pflush(); wait_a_while(nap_msec); saywhat("s5; press any key to continue"); show_panel(p5); pflush(); wait_a_while(nap_msec); saywhat("t3; press any key to continue"); top_panel(p3); pflush(); wait_a_while(nap_msec); saywhat("t1; press any key to continue"); top_panel(p1); pflush(); wait_a_while(nap_msec); saywhat("t2; press any key to continue"); top_panel(p2); pflush(); wait_a_while(nap_msec); saywhat("t3; press any key to continue"); top_panel(p3); pflush(); wait_a_while(nap_msec); saywhat("t4; press any key to continue"); top_panel(p4); pflush(); wait_a_while(nap_msec); for(itmp = 0; itmp < 6; itmp++) { saywhat("m4; press any key to continue"); wmove(w4, LINES/8, 1); waddstr(w4,mod[itmp]); move_panel(p4, LINES/6, itmp*(COLS/8)); wmove(w5, LINES/6, 1); waddstr(w5,mod[itmp]); pflush(); wait_a_while(nap_msec); saywhat("m5; press any key to continue"); wmove(w4, LINES/6, 1); waddstr(w4,mod[itmp]); move_panel(p5, LINES/3 - 1,(itmp*10) + 6); wmove(w5, LINES/8, 1); waddstr(w5,mod[itmp]); pflush(); wait_a_while(nap_msec); } saywhat("m4; press any key to continue"); move_panel(p4, LINES/6, itmp*(COLS/8)); pflush(); wait_a_while(nap_msec); saywhat("t5; press any key to continue"); top_panel(p5); pflush(); wait_a_while(nap_msec); saywhat("t2; press any key to continue"); top_panel(p2); pflush(); wait_a_while(nap_msec); saywhat("t1; press any key to continue"); top_panel(p1); pflush(); wait_a_while(nap_msec); saywhat("d2; press any key to continue"); rmpanel(p2); pflush(); wait_a_while(nap_msec); saywhat("h3; press any key to continue"); hide_panel(p3); pflush(); wait_a_while(nap_msec); saywhat("d1; press any key to continue"); rmpanel(p1); pflush(); wait_a_while(nap_msec); saywhat("d4; press any key to continue"); rmpanel(p4); pflush(); wait_a_while(nap_msec); saywhat("d5; press any key to continue"); rmpanel(p5); pflush(); wait_a_while(nap_msec); if(nap_msec == 1) break; nap_msec = 100L; } erase(); endwin(); } /**************************************************************************** * * Pad tester * ****************************************************************************/ #define GRIDSIZE 3 static bool show_panner_legend = TRUE; static int panner_legend(int line) { static const char *const legend[] = { "Use arrow keys (or U,D,L,R) to pan, q to quit (?,t,s flags)", "Use ! to shell-out. Toggle legend:?, timer:t, scroll mark:s.", "Use +,- (or j,k) to grow/shrink the panner vertically.", "Use <,> (or h,l) to grow/shrink the panner horizontally." }; int n = (SIZEOF(legend) - (LINES - line)); if (line < LINES && (n >= 0)) { move(line, 0); if (show_panner_legend) printw("%s", legend[n]); clrtoeol(); return show_panner_legend; } return FALSE; } static void panner_h_cleanup(int from_y, int from_x, int to_x) { if (!panner_legend(from_y)) do_h_line(from_y, from_x, ' ', to_x); } static void panner_v_cleanup(int from_y, int from_x, int to_y) { if (!panner_legend(from_y)) do_v_line(from_y, from_x, ' ', to_y); } static void panner(WINDOW *pad, int top_x, int top_y, int porty, int portx, int (*pgetc)(WINDOW *)) { #if HAVE_GETTIMEOFDAY struct timeval before, after; bool timing = TRUE; #endif bool scrollers = TRUE; int basex = 0; int basey = 0; int pxmax, pymax, lowend, highend, c; getmaxyx(pad, pymax, pxmax); scrollok(stdscr, FALSE); /* we don't want stdscr to scroll! */ c = KEY_REFRESH; do { #ifdef NCURSES_VERSION /* * During shell-out, the user may have resized the window. Adjust * the port size of the pad to accommodate this. Ncurses automatically * resizes all of the normal windows to fit on the new screen. */ if (top_x > COLS) top_x = COLS; if (portx > COLS) portx = COLS; if (top_y > LINES) top_y = LINES; if (porty > LINES) porty = LINES; #endif switch(c) { case KEY_REFRESH: erase(); /* FALLTHRU */ case '?': if (c == '?') show_panner_legend = !show_panner_legend; panner_legend(LINES - 4); panner_legend(LINES - 3); panner_legend(LINES - 2); panner_legend(LINES - 1); break; #if HAVE_GETTIMEOFDAY case 't': timing = !timing; if (!timing) panner_legend(LINES-1); break; #endif case 's': scrollers = !scrollers; break; /* Move the top-left corner of the pad, keeping the bottom-right * corner fixed. */ case 'h': /* increase-columns: move left edge to left */ if (top_x <= 0) beep(); else { panner_v_cleanup(top_y, top_x, porty); top_x--; } break; case 'j': /* decrease-lines: move top-edge down */ if (top_y >= porty) beep(); else { panner_h_cleanup(top_y - 1, top_x - (top_x > 0), portx); top_y++; } break; case 'k': /* increase-lines: move top-edge up */ if (top_y <= 0) beep(); else { top_y--; panner_h_cleanup(top_y, top_x, portx); } break; case 'l': /* decrease-columns: move left-edge to right */ if (top_x >= portx) beep(); else { panner_v_cleanup(top_y - (top_y > 0), top_x - 1, porty); top_x++; } break; /* Move the bottom-right corner of the pad, keeping the top-left * corner fixed. */ case KEY_IC: /* increase-columns: move right-edge to right */ if (portx >= pxmax || portx >= COLS) beep(); else { panner_v_cleanup(top_y - (top_y > 0), portx - 1, porty); ++portx; } break; case KEY_IL: /* increase-lines: move bottom-edge down */ if (porty >= pymax || porty >= LINES) beep(); else { panner_h_cleanup(porty - 1, top_x - (top_x > 0), portx); ++porty; } break; case KEY_DC: /* decrease-columns: move bottom edge up */ if (portx <= top_x) beep(); else { portx--; panner_v_cleanup(top_y - (top_y > 0), portx, porty); } break; case KEY_DL: /* decrease-lines */ if (porty <= top_y) beep(); else { porty--; panner_h_cleanup(porty, top_x - (top_x > 0), portx); } break; case KEY_LEFT: /* pan leftwards */ if (basex > 0) basex--; else beep(); break; case KEY_RIGHT: /* pan rightwards */ if (basex + portx - (pymax > porty) < pxmax) basex++; else beep(); break; case KEY_UP: /* pan upwards */ if (basey > 0) basey--; else beep(); break; case KEY_DOWN: /* pan downwards */ if (basey + porty - (pxmax > portx) < pymax) basey++; else beep(); break; default: beep(); break; } mvaddch(top_y - 1, top_x - 1, ACS_ULCORNER); do_v_line(top_y, top_x - 1, ACS_VLINE, porty); do_h_line(top_y - 1, top_x, ACS_HLINE, portx); if (scrollers && (pxmax > portx - 1)) { int length = (portx - top_x - 1); float ratio = ((float) length) / ((float) pxmax); lowend = top_x + (basex * ratio); highend = top_x + ((basex + length) * ratio); do_h_line(porty - 1, top_x, ACS_HLINE, lowend); if (highend < portx) { attron(A_REVERSE); do_h_line(porty - 1, lowend, ' ', highend + 1); attroff(A_REVERSE); do_h_line(porty - 1, highend + 1, ACS_HLINE, portx); } } else do_h_line(porty - 1, top_x, ACS_HLINE, portx); if (scrollers && (pymax > porty - 1)) { int length = (porty - top_y - 1); float ratio = ((float) length) / ((float) pymax); lowend = top_y + (basey * ratio); highend = top_y + ((basey + length) * ratio); do_v_line(top_y, portx - 1, ACS_VLINE, lowend); if (highend < porty) { attron(A_REVERSE); do_v_line(lowend, portx - 1, ' ', highend + 1); attroff(A_REVERSE); do_v_line(highend + 1, portx - 1, ACS_VLINE, porty); } } else do_v_line(top_y, portx - 1, ACS_VLINE, porty); mvaddch(top_y - 1, portx - 1, ACS_URCORNER); mvaddch(porty - 1, top_x - 1, ACS_LLCORNER); mvaddch(porty - 1, portx - 1, ACS_LRCORNER); #if HAVE_GETTIMEOFDAY gettimeofday(&before, 0); #endif wnoutrefresh(stdscr); pnoutrefresh(pad, basey, basex, top_y, top_x, porty - (pxmax > portx) - 1, portx - (pymax > porty) - 1); doupdate(); #if HAVE_GETTIMEOFDAY if (timing) { double elapsed; gettimeofday(&after, 0); elapsed = (after.tv_sec + after.tv_usec / 1.0e6) - (before.tv_sec + before.tv_usec / 1.0e6); move(LINES-1, COLS-20); printw("Secs: %2.03f", elapsed); refresh(); } #endif } while ((c = pgetc(pad)) != KEY_EXIT); scrollok(stdscr, TRUE); /* reset to driver's default */ } static int padgetch(WINDOW *win) { int c; while(1) switch(c = wGetchar(win)) { case '!': ShellOut(FALSE); return KEY_REFRESH; case CTRL('r'): endwin(); refresh(); return KEY_REFRESH; case CTRL('l'): return KEY_REFRESH; case 'U': return(KEY_UP); case 'D': return(KEY_DOWN); case 'R': return(KEY_RIGHT); case 'L': return(KEY_LEFT); case '+': return(KEY_IL); case '-': return(KEY_DL); case '>': return(KEY_IC); case '<': return(KEY_DC); case ERR: /* FALLTHRU */ case 'q': return(KEY_EXIT); default: return(c); } } static void demo_pad(void) /* Demonstrate pads. */ { int i, j; unsigned gridcount = 0; WINDOW *panpad = newpad(200, 200); for (i = 0; i < 200; i++) { for (j = 0; j < 200; j++) if (i % GRIDSIZE == 0 && j % GRIDSIZE == 0) { if (i == 0 || j == 0) waddch(panpad, '+'); else waddch(panpad, (chtype)('A' + (gridcount++ % 26))); } else if (i % GRIDSIZE == 0) waddch(panpad, '-'); else if (j % GRIDSIZE == 0) waddch(panpad, '|'); else waddch(panpad, ' '); } panner_legend(LINES - 4); panner_legend(LINES - 3); panner_legend(LINES - 2); panner_legend(LINES - 1); keypad(panpad, TRUE); /* Make the pad (initially) narrow enough that a trace file won't wrap. * We'll still be able to widen it during a test, since that's required * for testing boundaries. */ panner(panpad, 2, 2, LINES - 5, COLS-15, padgetch); delwin(panpad); endwin(); erase(); } #endif /* USE_LIBPANEL */ /**************************************************************************** * * Tests from John Burnell's PDCurses tester * ****************************************************************************/ static void Continue (WINDOW *win) { noecho(); wmove(win, 10, 1); mvwaddstr(win, 10, 1, " Press any key to continue"); wrefresh(win); wGetchar(win); } static void flushinp_test(WINDOW *win) /* Input test, adapted from John Burnell's PDCurses tester */ { int w, h, bx, by, sw, sh, i; WINDOW *subWin; wclear (win); w = win->_maxx; h = win->_maxy; bx = win->_begx; by = win->_begy; sw = w / 3; sh = h / 3; if((subWin = subwin(win, sh, sw, by + h - sh - 2, bx + w - sw - 2)) == 0) return; #ifdef A_COLOR if (has_colors()) { init_pair(2,COLOR_CYAN,COLOR_BLUE); wattrset(subWin, COLOR_PAIR(2) | A_BOLD); } else wattrset(subWin, A_BOLD); #else wattrset(subWin, A_BOLD); #endif box(subWin, ACS_VLINE, ACS_HLINE); mvwaddstr(subWin, 2, 1, "This is a subwindow"); wrefresh(win); nocbreak(); mvwaddstr(win, 0, 1, "This is a test of the flushinp() call."); mvwaddstr(win, 2, 1, "Type random keys for 5 seconds."); mvwaddstr(win, 3, 1, "These should be discarded (not echoed) after the subwindow goes away."); wrefresh(win); for (i = 0; i < 5; i++) { mvwprintw (subWin, 1, 1, "Time = %d", i); wrefresh(subWin); napms(1000); flushinp(); } delwin (subWin); werase(win); flash(); wrefresh(win); napms(1000); mvwaddstr(win, 2, 1, "If you were still typing when the window timer expired,"); mvwaddstr(win, 3, 1, "or else you typed nothing at all while it was running,"); mvwaddstr(win, 4, 1, "test was invalid. You'll see garbage or nothing at all. "); mvwaddstr(win, 6, 1, "Press a key"); wmove(win, 9, 10); wrefresh(win); echo(); wGetchar(win); flushinp(); mvwaddstr(win, 12, 0, "If you see any key other than what you typed, flushinp() is broken."); Continue(win); wmove(win, 9, 10); wdelch(win); wrefresh(win); wmove(win, 12, 0); clrtoeol(); waddstr(win, "What you typed should now have been deleted; if not, wdelch() failed."); Continue(win); cbreak(); } /**************************************************************************** * * Menu test * ****************************************************************************/ #if USE_LIBMENU #define MENU_Y 8 #define MENU_X 8 static int menu_virtualize(int c) { if (c == '\n' || c == KEY_EXIT) return(MAX_COMMAND + 1); else if (c == 'n' || c == KEY_DOWN) return(REQ_NEXT_ITEM); else if (c == 'p' || c == KEY_UP) return(REQ_PREV_ITEM); else if (c == ' ') return(REQ_TOGGLE_ITEM); else return(c); } static const char *animals[] = { "Lions", "Tigers", "Bears", "(Oh my!)", "Newts", "Platypi", "Lemurs", (char *)0 }; static void menu_test(void) { MENU *m; ITEM *items[SIZEOF(animals)]; ITEM **ip = items; const char **ap; int mrows, mcols; WINDOW *menuwin; mvaddstr(0, 0, "This is the menu test:"); mvaddstr(2, 0, " Use up and down arrow to move the select bar."); mvaddstr(3, 0, " 'n' and 'p' act like arrows."); mvaddstr(4, 0, " Press return to exit."); refresh(); for (ap = animals; *ap; ap++) *ip++ = new_item(*ap, ""); *ip = (ITEM *)0; m = new_menu(items); scale_menu(m, &mrows, &mcols); menuwin = newwin(mrows + 2, mcols + 2, MENU_Y, MENU_X); set_menu_win(m, menuwin); keypad(menuwin, TRUE); box(menuwin, 0, 0); set_menu_sub(m, derwin(menuwin, mrows, mcols, 1, 1)); post_menu(m); while (menu_driver(m, menu_virtualize(wGetchar(menuwin))) != E_UNKNOWN_COMMAND) continue; (void) mvprintw(LINES - 2, 0, "You chose: %s\n", item_name(current_item(m))); (void) addstr("Press any key to continue..."); wGetchar(stdscr); unpost_menu(m); delwin(menuwin); free_menu(m); for (ip = items; *ip; ip++) free_item(*ip); } #ifdef TRACE #define T_TBL(name) { #name, name } static struct { const char *name; int mask; } t_tbl[] = { T_TBL(TRACE_DISABLE), T_TBL(TRACE_TIMES), T_TBL(TRACE_TPUTS), T_TBL(TRACE_UPDATE), T_TBL(TRACE_MOVE), T_TBL(TRACE_CHARPUT), T_TBL(TRACE_ORDINARY), T_TBL(TRACE_CALLS), T_TBL(TRACE_VIRTPUT), T_TBL(TRACE_IEVENT), T_TBL(TRACE_BITS), T_TBL(TRACE_ICALLS), T_TBL(TRACE_CCALLS), T_TBL(TRACE_MAXIMUM), { (char *)0, 0 } }; static char *tracetrace(int tlevel) { static char *buf; int n; if (buf == 0) { size_t need = 12; for (n = 0; t_tbl[n].name != 0; n++) need += strlen(t_tbl[n].name) + 2; buf = malloc(need); } sprintf(buf, "0x%02x = {", tlevel); if (tlevel == 0) { sprintf(buf + strlen(buf), "%s, ", t_tbl[0].name); } else { for (n = 1; t_tbl[n].name != 0; n++) if ((tlevel & t_tbl[n].mask) == t_tbl[n].mask) { strcat(buf, t_tbl[n].name); strcat(buf, ", "); } } if (buf[strlen(buf) - 2] == ',') buf[strlen(buf) - 2] = '\0'; return(strcat(buf,"}")); } /* fake a dynamically reconfigurable menu using the 0th entry to deselect * the others */ static int run_trace_menu(MENU *m) { ITEM **items; ITEM *i, **p; for (;;) { bool changed = FALSE; switch (menu_driver(m, menu_virtualize(wGetchar(menu_win(m))))) { case E_UNKNOWN_COMMAND: return FALSE; default: items = menu_items(m); i = current_item(m); if (i == items[0]) { if (item_value(i)) { for (p = items+1; *p != 0; p++) if (item_value(*p)) { set_item_value(*p, FALSE); changed = TRUE; } } } else { for (p = items+1; *p != 0; p++) if (item_value(*p)) { set_item_value(items[0], FALSE); changed = TRUE; break; } } if (!changed) return TRUE; } } } static void trace_set(void) /* interactively set the trace level */ { MENU *m; ITEM *items[SIZEOF(t_tbl)]; ITEM **ip = items; int mrows, mcols, newtrace; int n; WINDOW *menuwin; mvaddstr(0, 0, "Interactively set trace level:"); mvaddstr(2, 0, " Press space bar to toggle a selection."); mvaddstr(3, 0, " Use up and down arrow to move the select bar."); mvaddstr(4, 0, " Press return to set the trace level."); mvprintw(6, 0, "(Current trace level is %s)", tracetrace(_nc_tracing)); refresh(); for (n = 0; t_tbl[n].name != 0; n++) *ip++ = new_item(t_tbl[n].name, ""); *ip = (ITEM *)0; m = new_menu(items); set_menu_format(m, 0, 2); scale_menu(m, &mrows, &mcols); menu_opts_off(m, O_ONEVALUE); menuwin = newwin(mrows + 2, mcols + 2, MENU_Y, MENU_X); set_menu_win(m, menuwin); keypad(menuwin, TRUE); box(menuwin, 0, 0); set_menu_sub(m, derwin(menuwin, mrows, mcols, 1, 1)); post_menu(m); for (ip = menu_items(m); *ip; ip++) { int mask = t_tbl[item_index(*ip)].mask; if (mask == 0) set_item_value(*ip, _nc_tracing == 0); else if ((mask & _nc_tracing) == mask) set_item_value(*ip, TRUE); } while (run_trace_menu(m)) continue; newtrace = 0; for (ip = menu_items(m); *ip; ip++) if (item_value(*ip)) newtrace |= t_tbl[item_index(*ip)].mask; trace(newtrace); _tracef("trace level interactively set to %s", tracetrace(_nc_tracing)); (void) mvprintw(LINES - 2, 0, "Trace level is %s\n", tracetrace(_nc_tracing)); (void) addstr("Press any key to continue..."); wGetchar(stdscr); unpost_menu(m); delwin(menuwin); free_menu(m); for (ip = items; *ip; ip++) free_item(*ip); } #endif /* TRACE */ #endif /* USE_LIBMENU */ /**************************************************************************** * * Forms test * ****************************************************************************/ #if USE_LIBFORM static FIELD *make_label(int frow, int fcol, NCURSES_CONST char *label) { FIELD *f = new_field(1, strlen(label), frow, fcol, 0, 0); if (f) { set_field_buffer(f, 0, label); set_field_opts(f, field_opts(f) & ~O_ACTIVE); } return(f); } static FIELD *make_field(int frow, int fcol, int rows, int cols) { FIELD *f = new_field(rows, cols, frow, fcol, 0, 0); if (f) set_field_back(f, A_UNDERLINE); return(f); } static void display_form(FORM *f) { WINDOW *w; int rows, cols; scale_form(f, &rows, &cols); if ((w =newwin(rows+2, cols+4, 0, 0)) != (WINDOW *)0) { set_form_win(f, w); set_form_sub(f, derwin(w, rows, cols, 1, 2)); box(w, 0, 0); keypad(w, TRUE); } if (post_form(f) != E_OK) wrefresh(w); } static void erase_form(FORM *f) { WINDOW *w = form_win(f); WINDOW *s = form_sub(f); unpost_form(f); werase(w); wrefresh(w); delwin(s); delwin(w); } static int form_virtualize(WINDOW *w) { static int mode = REQ_INS_MODE; int c = wGetchar(w); switch(c) { case QUIT: case ESCAPE: return(MAX_FORM_COMMAND + 1); /* demo doesn't use these three, leave them in anyway as sample code */ case KEY_NPAGE: case CTRL('F'): return(REQ_NEXT_PAGE); case KEY_PPAGE: return(REQ_PREV_PAGE); case KEY_NEXT: case CTRL('N'): return(REQ_NEXT_FIELD); case KEY_PREVIOUS: case CTRL('P'): return(REQ_PREV_FIELD); case KEY_HOME: return(REQ_FIRST_FIELD); case KEY_END: case KEY_LL: return(REQ_LAST_FIELD); case CTRL('L'): return(REQ_LEFT_FIELD); case CTRL('R'): return(REQ_RIGHT_FIELD); case CTRL('U'): return(REQ_UP_FIELD); case CTRL('D'): return(REQ_DOWN_FIELD); case CTRL('W'): return(REQ_NEXT_WORD); case CTRL('B'): return(REQ_PREV_WORD); case CTRL('S'): return(REQ_BEG_FIELD); case CTRL('E'): return(REQ_END_FIELD); case KEY_LEFT: return(REQ_LEFT_CHAR); case KEY_RIGHT: return(REQ_RIGHT_CHAR); case KEY_UP: return(REQ_UP_CHAR); case KEY_DOWN: return(REQ_DOWN_CHAR); case CTRL('M'): return(REQ_NEW_LINE); case CTRL('I'): return(REQ_INS_CHAR); case CTRL('O'): return(REQ_INS_LINE); case CTRL('V'): return(REQ_DEL_CHAR); case CTRL('H'): case KEY_BACKSPACE: return(REQ_DEL_PREV); case CTRL('Y'): return(REQ_DEL_LINE); case CTRL('G'): return(REQ_DEL_WORD); case CTRL('C'): return(REQ_CLR_EOL); case CTRL('K'): return(REQ_CLR_EOF); case CTRL('X'): return(REQ_CLR_FIELD); case CTRL('A'): return(REQ_NEXT_CHOICE); case CTRL('Z'): return(REQ_PREV_CHOICE); case CTRL(']'): if (mode == REQ_INS_MODE) return(mode = REQ_OVL_MODE); else return(mode = REQ_INS_MODE); default: return(c); } } static int my_form_driver(FORM *form, int c) { if (c == (MAX_FORM_COMMAND + 1) && form_driver(form, REQ_VALIDATION) == E_OK) return(TRUE); else { beep(); return(FALSE); } } static void demo_forms(void) { WINDOW *w; FORM *form; FIELD *f[10]; int finished = 0, c; mvaddstr(10, 57, "Forms Entry Test"); move(18, 0); addstr("Defined form-traversal keys: ^Q/ESC- exit form\n"); addstr("^N -- go to next field ^P -- go to previous field\n"); addstr("Home -- go to first field End -- go to last field\n"); addstr("^L -- go to field to left ^R -- go to field to right\n"); addstr("^U -- move upward to field ^D -- move downward to field\n"); addstr("^W -- go to next word ^B -- go to previous word\n"); addstr("^S -- go to start of field ^E -- go to end of field\n"); addstr("^H -- delete previous char ^Y -- delete line\n"); addstr("^G -- delete current word ^C -- clear to end of line\n"); addstr("^K -- clear to end of field ^X -- clear field\n"); addstr("Arrow keys move within a field as you would expect."); refresh(); /* describe the form */ f[0] = make_label(0, 15, "Sample Form"); f[1] = make_label(2, 0, "Last Name"); f[2] = make_field(3, 0, 1, 18); f[3] = make_label(2, 20, "First Name"); f[4] = make_field(3, 20, 1, 12); f[5] = make_label(2, 34, "Middle Name"); f[6] = make_field(3, 34, 1, 12); f[7] = make_label(5, 0, "Comments"); f[8] = make_field(6, 0, 4, 46); f[9] = (FIELD *)0; form = new_form(f); display_form(form); w = form_win(form); raw(); while (!finished) { switch(form_driver(form, c = form_virtualize(w))) { case E_OK: break; case E_UNKNOWN_COMMAND: finished = my_form_driver(form, c); break; default: beep(); break; } } erase_form(form); free_form(form); for (c = 0; f[c] != 0; c++) free_field(f[c]); noraw(); } #endif /* USE_LIBFORM */ /**************************************************************************** * * Overlap test * ****************************************************************************/ static void fillwin(WINDOW *win, char ch) { int y, x; for (y = 0; y <= win->_maxy; y++) { wmove(win, y, 0); for (x = 0; x <= win->_maxx; x++) waddch(win, ch); } } static void crosswin(WINDOW *win, char ch) { int y, x; for (y = 0; y <= win->_maxy; y++) { for (x = 0; x <= win->_maxx; x++) if (((x > win->_maxx / 3) && (x <= 2 * win->_maxx / 3)) || (((y > win->_maxy / 3) && (y <= 2 * win->_maxy / 3)))) { wmove(win, y, x); waddch(win, ch); } } } static void overlap_test(void) /* test effects of overlapping windows */ { int ch; WINDOW *win1 = newwin(9, 20, 3, 3); WINDOW *win2 = newwin(9, 20, 9, 16); refresh(); move(0, 0); printw("This test shows the behavior of wnoutrefresh() with respect to\n"); printw("the shared region of two overlapping windows A and B. The cross\n"); printw("pattern in each window does not overlap the other.\n"); move(18, 0); printw("a = refresh A, then B, then doupdate. b = refresh B, then A, then doupdaute\n"); printw("c = fill window A with letter A. d = fill window B with letter B.\n"); printw("e = cross pattern in window A. f = cross pattern in window B.\n"); printw("g = clear window A. h = clear window B.\n"); printw("i = overwrite A onto B. j = overwrite B onto A.\n"); printw("^Q/ESC = terminate test."); while ((ch = Getchar()) != QUIT && ch != ESCAPE) switch (ch) { case 'a': /* refresh window A first, then B */ wnoutrefresh(win1); wnoutrefresh(win2); doupdate(); break; case 'b': /* refresh window B first, then A */ wnoutrefresh(win2); wnoutrefresh(win1); doupdate(); break; case 'c': /* fill window A so it's visible */ fillwin(win1, 'A'); break; case 'd': /* fill window B so it's visible */ fillwin(win2, 'B'); break; case 'e': /* cross test pattern in window A */ crosswin(win1, 'A'); break; case 'f': /* cross test pattern in window A */ crosswin(win2, 'B'); break; case 'g': /* clear window A */ wclear(win1); wmove(win1, 0, 0); break; case 'h': /* clear window B */ wclear(win2); wmove(win2, 0, 0); break; case 'i': /* overwrite A onto B */ overwrite(win1, win2); break; case 'j': /* overwrite B onto A */ overwrite(win2, win1); break; } delwin(win2); delwin(win1); erase(); endwin(); } /**************************************************************************** * * Main sequence * ****************************************************************************/ static bool do_single_test(const char c) /* perform a single specified test */ { switch (c) { case 'a': getch_test(); break; case 'b': attr_test(); break; case 'c': if (!has_colors()) Cannot("does not support color."); else color_test(); break; case 'd': if (!has_colors()) Cannot("does not support color."); else if (!can_change_color()) Cannot("has hardwired color values."); else color_edit(); break; case 'e': slk_test(); break; case 'f': acs_display(); break; #if USE_LIBPANEL case 'o': demo_panels(); break; #endif case 'g': acs_and_scroll(); break; case 'i': flushinp_test(stdscr); break; case 'k': test_sgr_attributes(); break; #if USE_LIBMENU case 'm': menu_test(); break; #endif #if USE_LIBPANEL case 'p': demo_pad(); break; #endif #if USE_LIBFORM case 'r': demo_forms(); break; #endif case 's': overlap_test(); break; #if USE_LIBMENU && defined(TRACE) case 't': trace_set(); break; #endif case '?': (void) puts("This is the ncurses capability tester."); (void) puts("You may select a test from the main menu by typing the"); (void) puts("key letter of the choice (the letter to left of the =)"); (void) puts("at the > prompt. The commands `x' or `q' will exit."); break; default: return FALSE; } return TRUE; } static void usage(void) { static const char *const tbl[] = { "Usage: ncurses [options]" ,"" ,"Options:" ," -e fmt specify format for soft-keys test (e)" ," -f rip-off footer line (can repeat)" ," -h rip-off header line (can repeat)" ," -s msec specify nominal time for panel-demo (default: 1, to hold)" #ifdef TRACE ," -t mask specify default trace-level (may toggle with ^T)" #endif }; size_t n; for (n = 0; n < sizeof(tbl)/sizeof(tbl[0]); n++) fprintf(stderr, "%s\n", tbl[n]); exit(EXIT_FAILURE); } static void set_terminal_modes(void) { noraw(); cbreak(); noecho(); scrollok(stdscr, TRUE); idlok(stdscr, TRUE); keypad(stdscr, TRUE); } #ifdef SIGUSR1 static RETSIGTYPE announce_sig(int sig) { (void) fprintf(stderr, "Handled signal %d\r\n", sig); } #endif static int rip_footer(WINDOW *win, int columns) { wbkgd(win, A_REVERSE); werase(win); wmove(win, 0, 0); wprintw(win, "footer: %d columns", columns); wnoutrefresh(win); return OK; } static int rip_header(WINDOW *win, int columns) { wbkgd(win, A_REVERSE); werase(win); wmove(win, 0, 0); wprintw(win, "header: %d columns", columns); wnoutrefresh(win); return OK; } /*+------------------------------------------------------------------------- main(argc,argv) --------------------------------------------------------------------------*/ int main(int argc, char *argv[]) { int command, c; int my_e_param = 1; while ((c = getopt(argc, argv, "e:fhs:t:")) != EOF) { switch (c) { case 'e': my_e_param = atoi(optarg); #ifdef NCURSES_VERSION if (my_e_param > 3) /* allow extended layouts */ usage(); #else if (my_e_param > 1) usage(); #endif break; case 'f': ripoffline(-1, rip_footer); break; case 'h': ripoffline(1, rip_header); break; case 's': nap_msec = atol(optarg); break; #ifdef TRACE case 't': save_trace = atoi(optarg); break; #endif default: usage(); } } /* * If there's no menus (unlikely for ncurses!), then we'll have to set * tracing on initially, just in case the user wants to test something that * doesn't involve wGetchar. */ #ifdef TRACE /* enable debugging */ #if !USE_LIBMENU trace(save_trace); #else if (!isatty(fileno(stdin))) trace(save_trace); #endif /* USE_LIBMENU */ #endif /* TRACE */ /* tell it we're going to play with soft keys */ slk_init(my_e_param); #ifdef SIGUSR1 /* set up null signal catcher so we can see what interrupts to getch do */ signal(SIGUSR1, announce_sig); #endif /* we must initialize the curses data structure only once */ initscr(); bkgdset(BLANK); /* tests, in general, will want these modes */ start_color(); set_terminal_modes(); def_prog_mode(); /* * Return to terminal mode, so we're guaranteed of being able to * select terminal commands even if the capabilities are wrong. */ endwin(); (void) puts("Welcome to ncurses. Press ? for help."); do { (void) puts("This is the ncurses main menu"); (void) puts("a = keyboard and mouse input test"); (void) puts("b = character attribute test"); (void) puts("c = color test pattern"); (void) puts("d = edit RGB color values"); (void) puts("e = exercise soft keys"); (void) puts("f = display ACS characters"); (void) puts("g = display windows and scrolling"); (void) puts("i = test of flushinp()"); (void) puts("k = display character attributes"); #if USE_LIBMENU (void) puts("m = menu code test"); #endif #if USE_LIBPANEL (void) puts("o = exercise panels library"); (void) puts("p = exercise pad features"); (void) puts("q = quit"); #endif #if USE_LIBFORM (void) puts("r = exercise forms code"); #endif (void) puts("s = overlapping-refresh test"); #if USE_LIBMENU && defined(TRACE) (void) puts("t = set trace level"); #endif (void) puts("? = repeat this command summary"); (void) fputs("> ", stdout); (void) fflush(stdout); /* necessary under SVr4 curses */ /* * This used to be an 'fgets()' call. However (on Linux, at least) * mixing stream I/O and 'read()' (used in the library) causes the * input stream to be flushed when switching between the two. */ command = 0; for(;;) { char ch; if (read(fileno(stdin), &ch, 1) <= 0) { if (command == 0) command = 'q'; break; } else if (command == 0 && !isspace(ch)) { command = ch; } else if (ch == '\n' || ch == '\r') { if (command != 0) break; (void) fputs("> ", stdout); (void) fflush(stdout); } } if (do_single_test(command)) { /* * This may be overkill; it's intended to reset everything back * to the initial terminal modes so that tests don't get in * each other's way. */ flushinp(); set_terminal_modes(); reset_prog_mode(); clear(); refresh(); endwin(); continue; } } while (command != 'q'); ExitProgram(EXIT_SUCCESS); } /* ncurses.c ends here */