/* * A terminal driver using the curses library * * $Header: /usr/build/vile/vile/RCS/curses.c,v 1.20 2002/12/22 19:21:27 tom Exp $ */ #include "estruct.h" #include "edef.h" #if DISP_CURSES #define NPAUSE 10 /* # times thru update to pause */ #undef WINDOW #include "tcap.h" #define is_default(color) (color < 0 || color == 255) #if OPT_COLOR /* ANSI: black, red, green, yellow, blue, magenta, cyan, white */ static const char ANSI_palette[] = {"0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15"}; #endif /* OPT_COLOR */ static const struct { char *capname; int code; } keyseqs[] = { /* *INDENT-OFF* */ /* Arrow keys */ { CAPNAME("ku", "kcuu1"), KEY_Up }, { CAPNAME("kd", "kcud1"), KEY_Down }, { CAPNAME("kr", "kcuf1"), KEY_Right }, { CAPNAME("kl", "kcub1"), KEY_Left }, /* other cursor-movement */ { CAPNAME("kh", "khome"), KEY_Home }, { CAPNAME("kH", "kll"), KEY_End }, { CAPNAME("@7", "kend"), KEY_End }, /* page scroll */ { CAPNAME("kN", "knp"), KEY_Next }, { CAPNAME("kP", "kpp"), KEY_Prior }, /* editing */ { CAPNAME("kI", "kich1"), KEY_Insert }, { CAPNAME("kD", "kdch1"), KEY_Delete }, { CAPNAME("@0", "kfnd"), KEY_Find }, { CAPNAME("*6", "kslt"), KEY_Select }, /* command */ { CAPNAME("%1", "khlp"), KEY_Help }, /* function keys */ { CAPNAME("k1", "kf1"), KEY_F1 }, { CAPNAME("k2", "kf2"), KEY_F2 }, { CAPNAME("k3", "kf3"), KEY_F3 }, { CAPNAME("k4", "kf4"), KEY_F4 }, { CAPNAME("k5", "kf5"), KEY_F5 }, { CAPNAME("k6", "kf6"), KEY_F6 }, { CAPNAME("k7", "kf7"), KEY_F7 }, { CAPNAME("k8", "kf8"), KEY_F8 }, { CAPNAME("k9", "kf9"), KEY_F9 }, { CAPNAME("k;", "kf10"), KEY_F10 }, { CAPNAME("F1", "kf11"), KEY_F11 }, { CAPNAME("F2", "kf12"), KEY_F12 }, { CAPNAME("F3", "kf13"), KEY_F13 }, { CAPNAME("F4", "kf14"), KEY_F14 }, { CAPNAME("F5", "kf15"), KEY_F15 }, { CAPNAME("F6", "kf16"), KEY_F16 }, { CAPNAME("F7", "kf17"), KEY_F17 }, { CAPNAME("F8", "kf18"), KEY_F18 }, { CAPNAME("F9", "kf19"), KEY_F19 }, { CAPNAME("FA", "kf20"), KEY_F20 }, { CAPNAME("FB", "kf21"), KEY_F21 }, { CAPNAME("FC", "kf22"), KEY_F22 }, { CAPNAME("FD", "kf23"), KEY_F23 }, { CAPNAME("FE", "kf24"), KEY_F24 }, { CAPNAME("FF", "kf25"), KEY_F25 }, { CAPNAME("FG", "kf26"), KEY_F26 }, { CAPNAME("FH", "kf27"), KEY_F27 }, { CAPNAME("FI", "kf28"), KEY_F28 }, { CAPNAME("FJ", "kf29"), KEY_F29 }, { CAPNAME("FK", "kf30"), KEY_F30 }, { CAPNAME("FL", "kf31"), KEY_F31 }, { CAPNAME("FM", "kf32"), KEY_F32 }, { CAPNAME("FN", "kf33"), KEY_F33 }, { CAPNAME("FO", "kf34"), KEY_F34 }, { CAPNAME("FP", "kf35"), KEY_F35 } /* *INDENT-ON* */ }; #if SYS_OS2_EMX #include "os2keys.h" #endif static int in_screen = FALSE; static int can_color = FALSE; static void initialize(void) { size_t i; int j; static int already_open = 0; static char *fallback_arrows[] = { "\033O", /* SS3 */ "\033[", /* CSI */ "\217", /* SS3 */ "\233", /* CSI */ }; if (already_open) return; initscr(); raw(); noecho(); nonl(); nodelay(stdscr, TRUE); idlok(stdscr, TRUE); term.maxrows = term.rows = LINES; term.maxcols = term.cols = COLS; #if OPT_COLOR if (has_colors()) { can_color = TRUE; start_color(); #ifdef HAVE_USE_DEFAULT_COLORS use_default_colors(); #endif set_colors(COLORS); for (j = 1; j < COLOR_PAIRS; j++) { init_pair(j, j / COLORS, j % COLORS); } } else { set_colors(2); /* white/black */ } #endif #if OPT_COLOR set_palette(ANSI_palette); #endif /* * Provide fallback definitions for all ANSI/ISO/DEC cursor keys. */ for (i = 0; i < TABLESIZE(fallback_arrows); i++) { for (j = 'A'; j <= 'D'; j++) { char temp[80]; lsprintf(temp, "%s%c", fallback_arrows[i], j); addtosysmap(temp, strlen(temp), SPEC | j); } } #if SYS_OS2_EMX for (i = TABLESIZE(VIO_KeyMap); i--;) { addtosysmap(VIO_KeyMap[i].seq, 2, VIO_KeyMap[i].code); } #endif for (i = TABLESIZE(keyseqs); i--;) { char *seq = TGETSTR(keyseqs[i].capname, &p); if (!NO_CAP(seq)) { int len; TRACE(("TGETSTR(%s) = %s\n", keyseqs[i].capname, str_visible(seq))); #define DONT_MAP_DEL 1 #if DONT_MAP_DEL /* NetBSD, FreeBSD, etc. have the kD (delete) function key defined as the DEL char. i don't like this hack, but until we (and we may never) have separate system "map" and "map!" maps, we can't allow this -- DEL has different semantics in insert and command mode, whereas KEY_Delete has the same semantics (whatever they may be) in both. KEY_Delete is the only non-motion system map, by the way -- so the rest are benign in insert or command mode. */ if (strcmp(seq, "\177") == 0) continue; #endif addtosysmap(seq, len = strlen(seq), keyseqs[i].code); /* * Termcap represents nulls as octal 200, which is ambiguous * (ugh). To avoid losing escape sequences that may contain * nulls, check here, and add a mapping for the strings with * explicit nulls. */ #define TCAP_NULL '\200' if (strchr(seq, TCAP_NULL) != 0) { char temp[BUFSIZ]; (void) strcpy(temp, seq); for (j = 0; j < len; j++) if (char2int(temp[j]) == TCAP_NULL) temp[j] = '\0'; addtosysmap(temp, len, keyseqs[i].code); } } } ttopen(); already_open = TRUE; } static void curs_open(void) { TRACE(("curs_open\n")); vl_save_tty(); } static void curs_close(void) { TRACE(("curs_close\n")); vl_restore_tty(); } static int last_key = -1; static int curs_typahead(void) { if (last_key == -1) { nodelay(stdscr, TRUE); last_key = getch(); } return (last_key >= 0); } static int curs_getc(void) { int result = last_key; if (!in_screen) { fflush(stdout); result = getchar(); } else if (result == -1) { nodelay(stdscr, FALSE); result = getch(); } last_key = -1; TRACE(("curs_getc:%d%s\n", result, in_screen ? "" : "*")); return result; } static OUTC_DCL curs_putc(OUTC_ARGS) { c &= (N_chars - 1); if (in_screen) { OUTC_RET addch((UINT) (c)); } else { OUTC_RET putchar(c); } } static void curs_flush(void) { if (in_screen) { refresh(); TRACE(("curs_flush\n")); } } static void curs_kopen(void) { static int initialized = FALSE; if (!initialized) { initialized = TRUE; initialize(); } refresh(); in_screen = TRUE; TRACE(("curs_kopen\n")); } static void curs_kclose(void) { endwin(); in_screen = FALSE; TRACE(("curs_kclose\n")); } static void curs_move(register int row, register int col) { move(row, col); } static void curs_eeol(void) { clrtoeol(); } static void curs_eeop(void) { if (sgarbf) { erase(); wrefresh(curscr); } else { clrtobot(); } } /* move howmany lines starting at 'from' to 'to' */ static void curs_scroll(int from, int to, int howmany) { scrollok(stdscr, TRUE); if (from > to) { setscrreg(to, from + howmany - 1); } else if (from < to) { setscrreg(from, to + howmany - 1); } scrl(from - to); scrollok(stdscr, FALSE); } #if OPT_COLOR static void set_bkgd_colors(int fg, int bg) { int pair; #ifdef HAVE_USE_DEFAULT_COLORS /* * ncurses supports the use of default colors, which we can specify * in the init_pair() function at the expense of specifying all of the * allowable explicit color combinations. */ if (is_default(fg) && is_default(bg)) { pair = 0; fg = -1; bg = -1; } else { if (is_default(fg)) { pair = bg; fg = -1; } else if (is_default(bg)) { pair = fg * COLORS; bg = -1; } else { pair = fg * COLORS + bg; } } if (init_pair(pair, fg, bg) == ERR) return; #else pair = fg * COLORS + bg; #endif bkgdset(COLOR_PAIR(pair) | ' '); } static void curs_fcol(int color) { short fg, bg; int pair = PAIR_NUMBER(getattrs(stdscr)); pair_content(pair, &fg, &bg); set_bkgd_colors(color, bg); } static void curs_bcol(int color) { short fg, bg; int pair = PAIR_NUMBER(getattrs(stdscr)); pair_content(pair, &fg, &bg); set_bkgd_colors(fg, color); } static void curs_spal(const char *thePalette) { /* reset the palette registers */ set_ctrans(thePalette); } #endif /* OPT_COLOR */ #if OPT_VIDEO_ATTRS static void curs_attr(UINT attr) { chtype result = A_NORMAL; if (attr & VABOLD) result |= A_BOLD; if (attr & VAUL) result |= A_UNDERLINE; if (attr & (VAREV | VASEL)) result |= A_REVERSE; #if OPT_COLOR if (can_color) { int pair = PAIR_NUMBER(getattrs(stdscr)); short fg, bg; if (attr & VACOLOR) { fg = VCOLORNUM(attr); bg = gbcolor; #ifdef HAVE_USE_DEFAULT_COLORS if (is_default(bg)) { pair = fg * COLORS; init_pair(pair, fg, -1); } else { pair = fg * COLORS + bg; } #else pair = fg * COLORS + bg; #endif result |= COLOR_PAIR(pair); } } #endif attrset(result); } #else /* highlighting is a minimum attribute */ static void curs_rev( /* change reverse video status */ UINT state) { /* FALSE = normal video, TRUE = reverse video */ if (state) { standout(); } else { standend(); } } #endif /* OPT_VIDEO_ATTRS */ /* * Hide/show cursor. We do this in levels, so we can do the "right" thing with * multimotion. */ static void curs_cursor(int flag) { static int level; if (flag) { if (!++level) { TRACE(("CURSOR ON\n")); curs_set(1); } } else { if (!level--) { TRACE(("CURSOR OFF\n")); curs_set(0); } } } static void curs_beep(void) { #if OPT_FLASH if (global_g_val(GMDFLASH)) flash(); else #endif beep(); } TERM term = { 0, /* the first four values are set dynamically */ 0, 0, 0, NPAUSE, curs_open, curs_close, curs_kopen, curs_kclose, curs_getc, curs_putc, curs_typahead, curs_flush, curs_move, curs_eeol, curs_eeop, curs_beep, #if OPT_VIDEO_ATTRS curs_attr, #else curs_rev, #endif nullterm_setdescrip, #if OPT_COLOR curs_fcol, curs_bcol, curs_spal, #else nullterm_setfore, nullterm_setback, nullterm_setpal, #endif nullterm_setccol, curs_scroll, nullterm_pflush, nullterm_icursor, nullterm_settitle, ttwatchfd, ttunwatchfd, curs_cursor, }; #endif /* DISP_CURSES */