diff options
Diffstat (limited to 'pico/osdep/terminal.c')
-rw-r--r-- | pico/osdep/terminal.c | 1762 |
1 files changed, 1762 insertions, 0 deletions
diff --git a/pico/osdep/terminal.c b/pico/osdep/terminal.c new file mode 100644 index 00000000..72206c01 --- /dev/null +++ b/pico/osdep/terminal.c @@ -0,0 +1,1762 @@ +#if !defined(lint) && !defined(DOS) +static char rcsid[] = "$Id: terminal.c 921 2008-01-31 02:09:25Z hubert@u.washington.edu $"; +#endif + +/* + * ======================================================================== + * Copyright 2006-2008 University of Washington + * Copyright 2013 Eduardo Chappa + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * ======================================================================== + * + * Program: Display routines + */ + +#include <system.h> +#include <general.h> + + +#include "../estruct.h" +#include "../keydefs.h" +#include "../pico.h" +#include "../mode.h" + +#include "raw.h" +#include "color.h" +#include "tty.h" +#include "terminal.h" +#include "getkey.h" + +#ifndef _WINDOWS +extern long gmode; + + +/* + * Useful Defaults + */ +#define MARGIN 8 /* size of minimim margin and */ +#define MROW 2 /* rows in menu */ + + +/* any special properties of the current terminal */ +unsigned term_capabilities = (TT_EOLEXIST | TT_SCROLLEXIST | TT_INSCHAR | TT_DELCHAR); + + +/* + * + */ +unsigned +tthascap(void) +{ + return(term_capabilities); +} + + +#if HAS_TERMINFO + +/* + * terminfo-based terminal i/o and control routines + */ + + +/* internal prototypes */ +static int tinfomove(int, int); +static int tinfoeeol(void); +static int tinfoeeop(void); +static int tinfobeep(void); +static int tinforev(int); +static int tinfoopen(void); +static int tinfoterminalinfo(int); +static int tinfoclose(void); +static void setup_dflt_esc_seq(void); +static void tinfoinsert(UCS); +static void tinfodelete(void); + +extern int tput(); +extern int tputs(char *, int, int (*)(int)); +extern char *tgoto(char *, int, int); +extern char *tigetstr (); +extern int setupterm(char *, int, int *); +extern int tigetnum(char *); +extern int tigetflag(char *); + +/** + ** Note: The tgoto calls should really be replaced by tparm calls for + ** modern terminfo. tgoto(s, x, y) == tparm(s, y, x). + **/ + +int _tlines, _tcolumns; +char *_clearscreen, *_moveto, *_up, *_down, *_right, *_left, + *_setinverse, *_clearinverse, + *_setunderline, *_clearunderline, + *_setbold, *_clearallattr, /* there is no clear only bold! */ + *_cleartoeoln, *_cleartoeos, + *_deleteline, /* delete line */ + *_insertline, /* insert line */ + *_scrollregion, /* define a scrolling region, vt100 */ + *_insertchar, /* insert character, preferable to : */ + *_startinsert, /* set insert mode and, */ + *_endinsert, /* end insert mode */ + *_deletechar, /* delete character */ + *_startdelete, /* set delete mode and, */ + *_enddelete, /* end delete mode */ + *_scrolldown, /* scroll down */ + *_scrollup, /* scroll up */ + *_termcap_init, /* string to start termcap */ + *_termcap_end, /* string to end termcap */ + *_op, *_oc, *_setaf, *_setab, *_setf, *_setb, *_scp; +int _colors, _pairs, _bce; +char term_name[40]; + +TERM term = { + NROW-1, + NCOL, + MARGIN, + MROW, + tinfoopen, + tinfoterminalinfo, + tinfoclose, + ttgetc, + ttputc, + ttflush, + tinfomove, + tinfoeeol, + tinfoeeop, + tinfobeep, + tinforev +}; + + +/* + * Add default keypad sequences to the trie. + */ +static void +setup_dflt_esc_seq(void) +{ + /* + * this is sort of a hack [no kidding], but it allows us to use + * the function keys on pc's running telnet + */ + + /* + * UW-NDC/UCS vt10[02] application mode. + */ + kpinsert("\033OP", F1, 1); + kpinsert("\033OQ", F2, 1); + kpinsert("\033OR", F3, 1); + kpinsert("\033OS", F4, 1); + kpinsert("\033Op", F5, 1); + kpinsert("\033Oq", F6, 1); + kpinsert("\033Or", F7, 1); + kpinsert("\033Os", F8, 1); + kpinsert("\033Ot", F9, 1); + kpinsert("\033Ou", F10, 1); + kpinsert("\033Ov", F11, 1); + kpinsert("\033Ow", F12, 1); + + /* + * DEC vt100, ANSI and cursor key mode. + */ + kpinsert("\033OA", KEY_UP, 1); + kpinsert("\033OB", KEY_DOWN, 1); + kpinsert("\033OC", KEY_RIGHT, 1); + kpinsert("\033OD", KEY_LEFT, 1); + + /* + * special keypad functions + */ + kpinsert("\033[4J", KEY_PGUP, 1); + kpinsert("\033[3J", KEY_PGDN, 1); + kpinsert("\033[2J", KEY_HOME, 1); + kpinsert("\033[N", KEY_END, 1); + + /* + * vt220? + */ + kpinsert("\033[5~", KEY_PGUP, 1); + kpinsert("\033[6~", KEY_PGDN, 1); + kpinsert("\033[1~", KEY_HOME, 1); + kpinsert("\033[4~", KEY_END, 1); + + /* + * konsole, XTerm (XFree 4.x.x) keyboard setting + */ + kpinsert("\033[H", KEY_HOME, 1); + kpinsert("\033[F", KEY_END, 1); + + /* + * gnome-terminal 2.6.0, don't know why it + * changed from 2.2.1 + */ + kpinsert("\033OH", KEY_HOME, 1); + kpinsert("\033OF", KEY_END, 1); + + /* + * "\033[2~" was common for KEY_HOME in a quick survey + * of terminals (though typically the Insert key). + * Teraterm 2.33 sends the following escape sequences, + * which is quite incompatible with everything + * else: + * Home: "\033[2~" End: "\033[5~" PgUp: "\033[3~" + * PgDn: "\033[6~" + * The best thing to do would be to fix TeraTerm + * keymappings or to tweak terminfo. + */ + + /* + * ANSI mode. + */ + kpinsert("\033[=a", F1, 1); + kpinsert("\033[=b", F2, 1); + kpinsert("\033[=c", F3, 1); + kpinsert("\033[=d", F4, 1); + kpinsert("\033[=e", F5, 1); + kpinsert("\033[=f", F6, 1); + kpinsert("\033[=g", F7, 1); + kpinsert("\033[=h", F8, 1); + kpinsert("\033[=i", F9, 1); + kpinsert("\033[=j", F10, 1); + kpinsert("\033[=k", F11, 1); + kpinsert("\033[=l", F12, 1); + + /* + * DEC vt100, ANSI and cursor key mode reset. + */ + kpinsert("\033[A", KEY_UP, 1); + kpinsert("\033[B", KEY_DOWN, 1); + kpinsert("\033[C", KEY_RIGHT, 1); + kpinsert("\033[D", KEY_LEFT, 1); + + /* + * DEC vt52 mode. + */ + kpinsert("\033A", KEY_UP, 1); + kpinsert("\033B", KEY_DOWN, 1); + kpinsert("\033C", KEY_RIGHT, 1); + kpinsert("\033D", KEY_LEFT, 1); + + /* + * DEC vt52 application keys, and some Zenith 19. + */ + kpinsert("\033?r", KEY_DOWN, 1); + kpinsert("\033?t", KEY_LEFT, 1); + kpinsert("\033?v", KEY_RIGHT, 1); + kpinsert("\033?x", KEY_UP, 1); + + /* + * Some Ctrl-Arrow keys + */ + kpinsert("\033[5A", CTRL_KEY_UP, 1); + kpinsert("\033[5B", CTRL_KEY_DOWN, 1); + kpinsert("\033[5C", CTRL_KEY_RIGHT, 1); + kpinsert("\033[5D", CTRL_KEY_LEFT, 1); + + kpinsert("\033[1;5A", CTRL_KEY_UP, 1); + kpinsert("\033[1;5B", CTRL_KEY_DOWN, 1); + kpinsert("\033[1;5C", CTRL_KEY_RIGHT, 1); + kpinsert("\033[1;5D", CTRL_KEY_LEFT, 1); + + /* + * Map some shift+up/down/left/right to their shiftless counterparts + */ + kpinsert("\033[1;2A", KEY_UP, 1); + kpinsert("\033[1;2B", KEY_DOWN, 1); + kpinsert("\033[1;2C", KEY_RIGHT, 1); + kpinsert("\033[1;2D", KEY_LEFT, 1); + kpinsert("\033[a", KEY_UP, 1); + kpinsert("\033[b", KEY_DOWN, 1); + kpinsert("\033[c", KEY_RIGHT, 1); + kpinsert("\033[d", KEY_LEFT, 1); + + /* + * Sun Console sequences. + */ + kpinsert("\033[1", KEY_SWALLOW_Z, 1); + kpinsert("\033[215", KEY_SWAL_UP, 1); + kpinsert("\033[217", KEY_SWAL_LEFT, 1); + kpinsert("\033[219", KEY_SWAL_RIGHT, 1); + kpinsert("\033[221", KEY_SWAL_DOWN, 1); + + /* + * Kermit App Prog Cmd, gobble until ESC \ (kermit should intercept this) + */ + kpinsert("\033_", KEY_KERMIT, 1); + + /* + * Fake a control character. + */ + kpinsert("\033\033", KEY_DOUBLE_ESC, 1); +} + + +static int +tinfoterminalinfo(int termcap_wins) +{ + char *_ku, *_kd, *_kl, *_kr, + *_kppu, *_kppd, *_kphome, *_kpend, *_kpdel, + *_kf1, *_kf2, *_kf3, *_kf4, *_kf5, *_kf6, + *_kf7, *_kf8, *_kf9, *_kf10, *_kf11, *_kf12; + char *ttnm; + + if (Pmaster) { + /* + * setupterm() automatically retrieves the value + * of the TERM variable. + */ + int err; + ttnm = getenv("TERM"); + if(!ttnm) + return(-1); + + strncpy(term_name, ttnm, sizeof(term_name)); + term_name[sizeof(term_name)-1] = '\0'; + setupterm ((char *) 0, 1, &err); + if (err != 1) return(err-2); + } + else { + /* + * setupterm() issues a message and exits, if the + * terminfo data base is gone or the term type is + * unknown, if arg2 is 0. + */ + setupterm ((char *) 0, 1, (int *) 0); + } + + _clearscreen = tigetstr("clear"); + _moveto = tigetstr("cup"); + _up = tigetstr("cuu1"); + _down = tigetstr("cud1"); + _right = tigetstr("cuf1"); + _left = tigetstr("cub1"); + _setinverse = tigetstr("smso"); + _clearinverse = tigetstr("rmso"); + _setunderline = tigetstr("smul"); + _clearunderline = tigetstr("rmul"); + _setbold = tigetstr("bold"); + _clearallattr = tigetstr("sgr0"); + _cleartoeoln = tigetstr("el"); + _cleartoeos = tigetstr("ed"); + _deletechar = tigetstr("dch1"); + _insertchar = tigetstr("ich1"); + _startinsert = tigetstr("smir"); + _endinsert = tigetstr("rmir"); + _deleteline = tigetstr("dl1"); + _insertline = tigetstr("il1"); + _scrollregion = tigetstr("csr"); + _scrolldown = tigetstr("ind"); + _scrollup = tigetstr("ri"); + _termcap_init = tigetstr("smcup"); + _termcap_end = tigetstr("rmcup"); + _startdelete = tigetstr("smdc"); + _enddelete = tigetstr("rmdc"); + _ku = tigetstr("kcuu1"); + _kd = tigetstr("kcud1"); + _kl = tigetstr("kcub1"); + _kr = tigetstr("kcuf1"); + _kppu = tigetstr("kpp"); + _kppd = tigetstr("knp"); + _kphome = tigetstr("khome"); + _kpend = tigetstr("kend"); + _kpdel = tigetstr("kdch1"); + _kf1 = tigetstr("kf1"); + _kf2 = tigetstr("kf2"); + _kf3 = tigetstr("kf3"); + _kf4 = tigetstr("kf4"); + _kf5 = tigetstr("kf5"); + _kf6 = tigetstr("kf6"); + _kf7 = tigetstr("kf7"); + _kf8 = tigetstr("kf8"); + _kf9 = tigetstr("kf9"); + _kf10 = tigetstr("kf10"); + _kf11 = tigetstr("kf11"); + _kf12 = tigetstr("kf12"); + + _colors = tigetnum("colors"); + _pairs = tigetnum("pairs"); + _setaf = tigetstr("setaf"); + _setab = tigetstr("setab"); + _setf = tigetstr("setf"); + _setb = tigetstr("setb"); + _scp = tigetstr("scp"); + _op = tigetstr("op"); + _oc = tigetstr("oc"); + _bce = tigetflag("bce"); + + _tlines = tigetnum("lines"); + if(_tlines == -1){ + char *er; + int rr; + + /* tigetnum failed, try $LINES */ + er = getenv("LINES"); + if(er && (rr = atoi(er)) > 0) + _tlines = rr; + } + + _tcolumns = tigetnum("cols"); + if(_tcolumns == -1){ + char *ec; + int cc; + + /* tigetnum failed, try $COLUMNS */ + ec = getenv("COLUMNS"); + if(ec && (cc = atoi(ec)) > 0) + _tcolumns = cc; + } + + /* + * Add default keypad sequences to the trie. + * Since these come first, they will override any conflicting termcap + * or terminfo escape sequences defined below. An escape sequence is + * considered conflicting if one is a prefix of the other. + * So, without TERMCAP_WINS, there will likely be some termcap/terminfo + * escape sequences that don't work, because they conflict with default + * sequences defined here. + */ + if(!termcap_wins) + setup_dflt_esc_seq(); + + /* + * add termcap/info escape sequences to the trie... + */ + + if(_ku != NULL && _kd != NULL && _kl != NULL && _kr != NULL){ + kpinsert(_ku, KEY_UP, termcap_wins); + kpinsert(_kd, KEY_DOWN, termcap_wins); + kpinsert(_kl, KEY_LEFT, termcap_wins); + kpinsert(_kr, KEY_RIGHT, termcap_wins); + } + + if(_kppu != NULL && _kppd != NULL){ + kpinsert(_kppu, KEY_PGUP, termcap_wins); + kpinsert(_kppd, KEY_PGDN, termcap_wins); + } + + kpinsert(_kphome, KEY_HOME, termcap_wins); + kpinsert(_kpend, KEY_END, termcap_wins); + kpinsert(_kpdel, KEY_DEL, termcap_wins); + + kpinsert(_kf1, F1, termcap_wins); + kpinsert(_kf2, F2, termcap_wins); + kpinsert(_kf3, F3, termcap_wins); + kpinsert(_kf4, F4, termcap_wins); + kpinsert(_kf5, F5, termcap_wins); + kpinsert(_kf6, F6, termcap_wins); + kpinsert(_kf7, F7, termcap_wins); + kpinsert(_kf8, F8, termcap_wins); + kpinsert(_kf9, F9, termcap_wins); + kpinsert(_kf10, F10, termcap_wins); + kpinsert(_kf11, F11, termcap_wins); + kpinsert(_kf12, F12, termcap_wins); + + /* + * Add default keypad sequences to the trie. + * Since these come after the termcap/terminfo escape sequences above, + * the termcap/info sequences will override any conflicting default + * escape sequences defined here. + * So, with TERMCAP_WINS, some of the default sequences will be missing. + * This means that you'd better get all of your termcap/terminfo entries + * correct if you define TERMCAP_WINS. + */ + if(termcap_wins) + setup_dflt_esc_seq(); + + if(Pmaster) + return(0); + else + return(TRUE); +} + +static int +tinfoopen(void) +{ + int row, col; + + /* + * determine the terminal's communication speed and decide + * if we need to do optimization ... + */ + if(ttisslow()) + term_capabilities |= TT_OPTIMIZE; + + col = _tcolumns; + row = _tlines; + if(row >= 0) + row--; + + ttgetwinsz(&row, &col); + term.t_nrow = (short) row; + term.t_ncol = (short) col; + + if(_cleartoeoln != NULL) /* able to use clear to EOL? */ + term_capabilities |= TT_EOLEXIST; + else + term_capabilities &= ~TT_EOLEXIST; + + if(_setinverse != NULL) + term_capabilities |= TT_REVEXIST; + else + term_capabilities &= ~TT_REVEXIST; + + if(_deletechar == NULL && (_startdelete == NULL || _enddelete == NULL)) + term_capabilities &= ~TT_DELCHAR; + + if(_insertchar == NULL && (_startinsert == NULL || _endinsert == NULL)) + term_capabilities &= ~TT_INSCHAR; + + if((_scrollregion == NULL || _scrolldown == NULL || _scrollup == NULL) + && (_deleteline == NULL || _insertline == NULL)) + term_capabilities &= ~TT_SCROLLEXIST; + + if(_clearscreen == NULL || _moveto == NULL || _up == NULL){ + if(Pmaster == NULL){ + puts("Incomplete terminfo entry\n"); + exit(1); + } + } + + ttopen(); + + if(_termcap_init && !Pmaster) { + putpad(_termcap_init); /* any init terminfo requires */ + if (_scrollregion) + putpad(tgoto(_scrollregion, term.t_nrow, 0)) ; + } + + /* + * Initialize UW-modified NCSA telnet to use its functionkeys + */ + if((gmode & MDFKEY) && Pmaster == NULL) + puts("\033[99h"); + + /* return ignored */ + return(0); +} + + +static int +tinfoclose(void) +{ + if(!Pmaster){ + if(gmode&MDFKEY) + puts("\033[99l"); /* reset UW-NCSA telnet keys */ + + if(_termcap_end) /* any clean up terminfo requires */ + putpad(_termcap_end); + } + + ttclose(); + + /* return ignored */ + return(0); +} + + +/* + * tinfoinsert - insert a character at the current character position. + * _insertchar takes precedence. + */ +static void +tinfoinsert(UCS ch) +{ + if(_insertchar != NULL){ + putpad(_insertchar); + ttputc(ch); + } + else{ + putpad(_startinsert); + ttputc(ch); + putpad(_endinsert); + } +} + + +/* + * tinfodelete - delete a character at the current character position. + */ +static void +tinfodelete(void) +{ + if(_startdelete == NULL && _enddelete == NULL) + putpad(_deletechar); + else{ + putpad(_startdelete); + putpad(_deletechar); + putpad(_enddelete); + } +} + + +/* + * o_scrolldown() - open a line at the given row position. + * use either region scrolling or deleteline/insertline + * to open a new line. + */ +int +o_scrolldown(int row, int n) +{ + register int i; + + if(_scrollregion != NULL){ + putpad(tgoto(_scrollregion, term.t_nrow - (term.t_mrow+1), row)); + tinfomove(row, 0); + for(i = 0; i < n; i++) + putpad((_scrollup != NULL && *_scrollup != '\0') ? _scrollup : "\n" ); + putpad(tgoto(_scrollregion, term.t_nrow, 0)); + tinfomove(row, 0); + } + else{ + /* + * this code causes a jiggly motion of the keymenu when scrolling + */ + for(i = 0; i < n; i++){ + tinfomove(term.t_nrow - (term.t_mrow+1), 0); + putpad(_deleteline); + tinfomove(row, 0); + putpad(_insertline); + } +#ifdef NOWIGGLYLINES + /* + * this code causes a sweeping motion up and down the display + */ + tinfomove(term.t_nrow - term.t_mrow - n, 0); + for(i = 0; i < n; i++) + putpad(_deleteline); + tinfomove(row, 0); + for(i = 0; i < n; i++) + putpad(_insertline); +#endif + } + + /* return ignored */ + return(0); +} + + +/* + * o_scrollup() - open a line at the given row position. + * use either region scrolling or deleteline/insertline + * to open a new line. + */ +int +o_scrollup(int row, int n) +{ + register int i; + + if(_scrollregion != NULL){ + putpad(tgoto(_scrollregion, term.t_nrow - (term.t_mrow+1), row)); + /* setting scrolling region moves cursor to home */ + tinfomove(term.t_nrow-(term.t_mrow+1), 0); + for(i = 0;i < n; i++) + putpad((_scrolldown == NULL || _scrolldown[0] == '\0') ? "\n" + : _scrolldown); + putpad(tgoto(_scrollregion, term.t_nrow, 0)); + tinfomove(2, 0); + } + else{ + for(i = 0; i < n; i++){ + tinfomove(row, 0); + putpad(_deleteline); + tinfomove(term.t_nrow - (term.t_mrow+1), 0); + putpad(_insertline); + } +#ifdef NOWIGGLYLINES + /* see note above */ + tinfomove(row, 0); + for(i = 0; i < n; i++) + putpad(_deleteline); + tinfomove(term.t_nrow - term.t_mrow - n, 0); + for(i = 0;i < n; i++) + putpad(_insertline); +#endif + } + + /* return ignored */ + return(0); +} + + + +/* + * o_insert - use terminfo to optimized character insert + * returns: true if it optimized output, false otherwise + */ +int +o_insert(UCS c) +{ + if(term_capabilities & TT_INSCHAR){ + tinfoinsert(c); + return(1); /* no problems! */ + } + + return(0); /* can't do it. */ +} + + +/* + * o_delete - use terminfo to optimized character insert + * returns true if it optimized output, false otherwise + */ +int +o_delete(void) +{ + if(term_capabilities & TT_DELCHAR){ + tinfodelete(); + return(1); /* deleted, no problem! */ + } + + return(0); /* no dice. */ +} + + +static int +tinfomove(int row, int col) +{ + putpad(tgoto(_moveto, col, row)); + + /* return ignored */ + return(0); +} + + +static int +tinfoeeol(void) +{ + int c, starting_col, starting_line; + char *last_bg_color; + + /* + * If the terminal doesn't have back color erase, then we have to + * erase manually to preserve the background color. + */ + if(pico_usingcolor() && (!_bce || !_cleartoeoln)){ + extern int ttcol, ttrow; + + starting_col = ttcol; + starting_line = ttrow; + last_bg_color = pico_get_last_bg_color(); + pico_set_nbg_color(); + for(c = ttcol; c < term.t_ncol; c++) + ttputc(' '); + + tinfomove(starting_line, starting_col); + if(last_bg_color){ + pico_set_bg_color(last_bg_color); + free(last_bg_color); + } + } + else if(_cleartoeoln) + putpad(_cleartoeoln); + + /* return ignored */ + return(0); +} + + +static int +tinfoeeop(void) +{ + int i, starting_col, starting_row; + + /* + * If the terminal doesn't have back color erase, then we have to + * erase manually to preserve the background color. + */ + if(pico_usingcolor() && (!_bce || !_cleartoeos)){ + extern int ttcol, ttrow; + + starting_col = ttcol; + starting_row = ttrow; + tinfoeeol(); /* rest of this line */ + for(i = ttrow+1; i <= term.t_nrow; i++){ /* the remaining lines */ + tinfomove(i, 0); + tinfoeeol(); + } + + tinfomove(starting_row, starting_col); + } + else if(_cleartoeos) + putpad(_cleartoeos); + + /* return ignored */ + return(0); +} + + +static int +tinforev(int state) /* change reverse video status */ +{ /* FALSE = normal video, TRUE = rev video */ + if(state) + StartInverse(); + else + EndInverse(); + + return(1); +} + + +static int +tinfobeep(void) +{ + ttputc(BELL); + + /* return ignored */ + return(0); +} + + +void +putpad(char *str) +{ + tputs(str, 1, putchar); +} + +#elif HAS_TERMCAP + +/* + * termcap-based terminal i/o and control routines + */ + + +/* internal prototypes */ +static int tcapmove(int, int); +static int tcapeeol(void); +static int tcapeeop(void); +static int tcapbeep(void); +static int tcaprev(int); +static int tcapopen(void); +static int tcapterminalinfo(int); +static int tcapclose(void); +static void setup_dflt_esc_seq(void); +static void tcapinsert(UCS); +static void tcapdelete(void); + +extern int tput(); +extern char *tgoto(char *, int, int); + +/* + * This number used to be 315. No doubt there was a reason for that but we + * don't know what it was. It's a bit of a hassle to make it dynamic, and most + * modern systems seem to be using terminfo, so we'll just change it to 800. + * We weren't stopping on overflow before, so we'll do that, too. + */ +#define TCAPSLEN 800 +char tcapbuf[TCAPSLEN]; +int _tlines, _tcolumns; +char *_clearscreen, *_moveto, *_up, *_down, *_right, *_left, + *_setinverse, *_clearinverse, + *_setunderline, *_clearunderline, + *_setbold, *_clearallattr, /* there is no clear only bold! */ + *_cleartoeoln, *_cleartoeos, + *_deleteline, /* delete line */ + *_insertline, /* insert line */ + *_scrollregion, /* define a scrolling region, vt100 */ + *_insertchar, /* insert character, preferable to : */ + *_startinsert, /* set insert mode and, */ + *_endinsert, /* end insert mode */ + *_deletechar, /* delete character */ + *_startdelete, /* set delete mode and, */ + *_enddelete, /* end delete mode */ + *_scrolldown, /* scroll down */ + *_scrollup, /* scroll up */ + *_termcap_init, /* string to start termcap */ + *_termcap_end, /* string to end termcap */ + *_op, *_oc, *_setaf, *_setab, *_setf, *_setb, *_scp; +int _colors, _pairs, _bce; +char term_name[40]; + +TERM term = { + NROW-1, + NCOL, + MARGIN, + MROW, + tcapopen, + tcapterminalinfo, + tcapclose, + ttgetc, + ttputc, + ttflush, + tcapmove, + tcapeeol, + tcapeeop, + tcapbeep, + tcaprev +}; + + +/* + * Add default keypad sequences to the trie. + */ +static void +setup_dflt_esc_seq(void) +{ + /* + * this is sort of a hack, but it allows us to use + * the function keys on pc's running telnet + */ + + /* + * UW-NDC/UCS vt10[02] application mode. + */ + kpinsert("\033OP", F1, 1); + kpinsert("\033OQ", F2, 1); + kpinsert("\033OR", F3, 1); + kpinsert("\033OS", F4, 1); + kpinsert("\033Op", F5, 1); + kpinsert("\033Oq", F6, 1); + kpinsert("\033Or", F7, 1); + kpinsert("\033Os", F8, 1); + kpinsert("\033Ot", F9, 1); + kpinsert("\033Ou", F10, 1); + kpinsert("\033Ov", F11, 1); + kpinsert("\033Ow", F12, 1); + + /* + * DEC vt100, ANSI and cursor key mode. + */ + kpinsert("\033OA", KEY_UP, 1); + kpinsert("\033OB", KEY_DOWN, 1); + kpinsert("\033OC", KEY_RIGHT, 1); + kpinsert("\033OD", KEY_LEFT, 1); + + /* + * special keypad functions + */ + kpinsert("\033[4J", KEY_PGUP, 1); + kpinsert("\033[3J", KEY_PGDN, 1); + kpinsert("\033[2J", KEY_HOME, 1); + kpinsert("\033[N", KEY_END, 1); + + /* + * vt220? + */ + kpinsert("\033[5~", KEY_PGUP, 1); + kpinsert("\033[6~", KEY_PGDN, 1); + kpinsert("\033[1~", KEY_HOME, 1); + kpinsert("\033[4~", KEY_END, 1); + + /* + * konsole, XTerm (XFree 4.x.x) keyboard setting + */ + kpinsert("\033[H", KEY_HOME, 1); + kpinsert("\033[F", KEY_END, 1); + + /* + * gnome-terminal 2.6.0, don't know why it + * changed from 2.2.1 + */ + kpinsert("\033OH", KEY_HOME, 1); + kpinsert("\033OF", KEY_END, 1); + + /* + * "\033[2~" was common for KEY_HOME in a quick survey + * of terminals (though typically the Insert key). + * Teraterm 2.33 sends the following escape sequences, + * which is quite incompatible with everything + * else: + * Home: "\033[2~" End: "\033[5~" PgUp: "\033[3~" + * PgDn: "\033[6~" + * The best thing to do would be to fix TeraTerm + * keymappings or to tweak terminfo. + */ + + /* + * ANSI mode. + */ + kpinsert("\033[=a", F1, 1); + kpinsert("\033[=b", F2, 1); + kpinsert("\033[=c", F3, 1); + kpinsert("\033[=d", F4, 1); + kpinsert("\033[=e", F5, 1); + kpinsert("\033[=f", F6, 1); + kpinsert("\033[=g", F7, 1); + kpinsert("\033[=h", F8, 1); + kpinsert("\033[=i", F9, 1); + kpinsert("\033[=j", F10, 1); + kpinsert("\033[=k", F11, 1); + kpinsert("\033[=l", F12, 1); + + /* + * DEC vt100, ANSI and cursor key mode reset. + */ + kpinsert("\033[A", KEY_UP, 1); + kpinsert("\033[B", KEY_DOWN, 1); + kpinsert("\033[C", KEY_RIGHT, 1); + kpinsert("\033[D", KEY_LEFT, 1); + + /* + * DEC vt52 mode. + */ + kpinsert("\033A", KEY_UP, 1); + kpinsert("\033B", KEY_DOWN, 1); + kpinsert("\033C", KEY_RIGHT, 1); + kpinsert("\033D", KEY_LEFT, 1); + + /* + * DEC vt52 application keys, and some Zenith 19. + */ + kpinsert("\033?r", KEY_DOWN, 1); + kpinsert("\033?t", KEY_LEFT, 1); + kpinsert("\033?v", KEY_RIGHT, 1); + kpinsert("\033?x", KEY_UP, 1); + + /* + * Some Ctrl-Arrow keys + */ + kpinsert("\033[5A", CTRL_KEY_UP, 1); + kpinsert("\033[5B", CTRL_KEY_DOWN, 1); + kpinsert("\033[5C", CTRL_KEY_RIGHT, 1); + kpinsert("\033[5D", CTRL_KEY_LEFT, 1); + + kpinsert("\033[1;5A", CTRL_KEY_UP, 1); + kpinsert("\033[1;5B", CTRL_KEY_DOWN, 1); + kpinsert("\033[1;5C", CTRL_KEY_RIGHT, 1); + kpinsert("\033[1;5D", CTRL_KEY_LEFT, 1); + + /* + * Map some shift+up/down/left/right to their shiftless counterparts + */ + kpinsert("\033[1;2A", KEY_UP, 1); + kpinsert("\033[1;2B", KEY_DOWN, 1); + kpinsert("\033[1;2C", KEY_RIGHT, 1); + kpinsert("\033[1;2D", KEY_LEFT, 1); + kpinsert("\033[a", KEY_UP, 1); + kpinsert("\033[b", KEY_DOWN, 1); + kpinsert("\033[c", KEY_RIGHT, 1); + kpinsert("\033[d", KEY_LEFT, 1); + + /* + * Sun Console sequences. + */ + kpinsert("\033[1", KEY_SWALLOW_Z, 1); + kpinsert("\033[215", KEY_SWAL_UP, 1); + kpinsert("\033[217", KEY_SWAL_LEFT, 1); + kpinsert("\033[219", KEY_SWAL_RIGHT, 1); + kpinsert("\033[221", KEY_SWAL_DOWN, 1); + + /* + * Kermit App Prog Cmd, gobble until ESC \ (kermit should intercept this) + */ + kpinsert("\033_", KEY_KERMIT, 1); + + /* + * Fake a control character. + */ + kpinsert("\033\033", KEY_DOUBLE_ESC, 1); +} + + +/* + * Read termcap and set some global variables. Initialize input trie to + * decode escape sequences. + */ +static int +tcapterminalinfo(int termcap_wins) +{ + char *p, *tgetstr(); + char tcbuf[2*1024]; + char *tv_stype; + char err_str[72]; + int err; + char *_ku, *_kd, *_kl, *_kr, + *_kppu, *_kppd, *_kphome, *_kpend, *_kpdel, + *_kf1, *_kf2, *_kf3, *_kf4, *_kf5, *_kf6, + *_kf7, *_kf8, *_kf9, *_kf10, *_kf11, *_kf12; + + if (!(tv_stype = getenv("TERM")) || !strncpy(term_name, tv_stype, sizeof(term_name))){ + if(Pmaster){ + return(-1); + } + else{ + puts("Environment variable TERM not defined!"); + exit(1); + } + } + + term_name[sizeof(term_name)-1] = '\0'; + + if((err = tgetent(tcbuf, tv_stype)) != 1){ + if(Pmaster){ + return(err - 2); + } + else{ + snprintf(err_str, sizeof(err_str), "Unknown terminal type %s!", tv_stype); + puts(err_str); + exit(1); + } + } + + p = tcapbuf; + + _clearscreen = tgetstr("cl", &p); + _moveto = tgetstr("cm", &p); + _up = tgetstr("up", &p); + _down = tgetstr("do", &p); + _right = tgetstr("nd", &p); + _left = tgetstr("bs", &p); + _setinverse = tgetstr("so", &p); + _clearinverse = tgetstr("se", &p); + _setunderline = tgetstr("us", &p); + _clearunderline = tgetstr("ue", &p); + _setbold = tgetstr("md", &p); + _clearallattr = tgetstr("me", &p); + _cleartoeoln = tgetstr("ce", &p); + _cleartoeos = tgetstr("cd", &p); + _deletechar = tgetstr("dc", &p); + _insertchar = tgetstr("ic", &p); + _startinsert = tgetstr("im", &p); + _endinsert = tgetstr("ei", &p); + _deleteline = tgetstr("dl", &p); + _insertline = tgetstr("al", &p); + _scrollregion = tgetstr("cs", &p); + _scrolldown = tgetstr("sf", &p); + _scrollup = tgetstr("sr", &p); + _termcap_init = tgetstr("ti", &p); + _termcap_end = tgetstr("te", &p); + _startdelete = tgetstr("dm", &p); + _enddelete = tgetstr("ed", &p); + _ku = tgetstr("ku", &p); + _kd = tgetstr("kd", &p); + _kl = tgetstr("kl", &p); + _kr = tgetstr("kr", &p); + _kppu = tgetstr("kP", &p); + _kppd = tgetstr("kN", &p); + _kphome = tgetstr("kh", &p); + _kpend = tgetstr("kH", &p); + _kpdel = tgetstr("kD", &p); + _kf1 = tgetstr("k1", &p); + _kf2 = tgetstr("k2", &p); + _kf3 = tgetstr("k3", &p); + _kf4 = tgetstr("k4", &p); + _kf5 = tgetstr("k5", &p); + _kf6 = tgetstr("k6", &p); + _kf7 = tgetstr("k7", &p); + _kf8 = tgetstr("k8", &p); + _kf9 = tgetstr("k9", &p); + if((_kf10 = tgetstr("k;", &p)) == NULL) + _kf10 = tgetstr("k0", &p); + _kf11 = tgetstr("F1", &p); + _kf12 = tgetstr("F2", &p); + + _colors = tgetnum("Co"); + _pairs = tgetnum("pa"); + _setaf = tgetstr("AF", &p); + _setab = tgetstr("AB", &p); + _setf = tgetstr("Sf", &p); + _setb = tgetstr("Sb", &p); + _scp = tgetstr("sp", &p); + _op = tgetstr("op", &p); + _oc = tgetstr("oc", &p); + _bce = tgetflag("ut"); + + if (p >= &tcapbuf[TCAPSLEN]){ + puts("Terminal description too big!\n"); + if(Pmaster) + return(-3); + else + exit(1); + } + + _tlines = tgetnum("li"); + if(_tlines == -1){ + char *er; + int rr; + + /* tgetnum failed, try $LINES */ + er = getenv("LINES"); + if(er && (rr = atoi(er)) > 0) + _tlines = rr; + } + + _tcolumns = tgetnum("co"); + if(_tcolumns == -1){ + char *ec; + int cc; + + /* tgetnum failed, try $COLUMNS */ + ec = getenv("COLUMNS"); + if(ec && (cc = atoi(ec)) > 0) + _tcolumns = cc; + } + + /* + * Add default keypad sequences to the trie. + * Since these come first, they will override any conflicting termcap + * or terminfo escape sequences defined below. An escape sequence is + * considered conflicting if one is a prefix of the other. + * So, without TERMCAP_WINS, there will likely be some termcap/terminfo + * escape sequences that don't work, because they conflict with default + * sequences defined here. + */ + if(!termcap_wins) + setup_dflt_esc_seq(); + + /* + * add termcap/info escape sequences to the trie... + */ + + if(_ku != NULL && _kd != NULL && _kl != NULL && _kr != NULL){ + kpinsert(_ku, KEY_UP, termcap_wins); + kpinsert(_kd, KEY_DOWN, termcap_wins); + kpinsert(_kl, KEY_LEFT, termcap_wins); + kpinsert(_kr, KEY_RIGHT, termcap_wins); + } + + if(_kppu != NULL && _kppd != NULL){ + kpinsert(_kppu, KEY_PGUP, termcap_wins); + kpinsert(_kppd, KEY_PGDN, termcap_wins); + } + + kpinsert(_kphome, KEY_HOME, termcap_wins); + kpinsert(_kpend, KEY_END, termcap_wins); + kpinsert(_kpdel, KEY_DEL, termcap_wins); + + kpinsert(_kf1, F1, termcap_wins); + kpinsert(_kf2, F2, termcap_wins); + kpinsert(_kf3, F3, termcap_wins); + kpinsert(_kf4, F4, termcap_wins); + kpinsert(_kf5, F5, termcap_wins); + kpinsert(_kf6, F6, termcap_wins); + kpinsert(_kf7, F7, termcap_wins); + kpinsert(_kf8, F8, termcap_wins); + kpinsert(_kf9, F9, termcap_wins); + kpinsert(_kf10, F10, termcap_wins); + kpinsert(_kf11, F11, termcap_wins); + kpinsert(_kf12, F12, termcap_wins); + + /* + * Add default keypad sequences to the trie. + * Since these come after the termcap/terminfo escape sequences above, + * the termcap/info sequences will override any conflicting default + * escape sequences defined here. + * So, with TERMCAP_WINS, some of the default sequences will be missing. + * This means that you'd better get all of your termcap/terminfo entries + * correct if you define TERMCAP_WINS. + */ + if(termcap_wins) + setup_dflt_esc_seq(); + + if(Pmaster) + return(0); + else + return(TRUE); +} + +static int +tcapopen(void) +{ + int row, col; + + /* + * determine the terminal's communication speed and decide + * if we need to do optimization ... + */ + if(ttisslow()) + term_capabilities |= TT_OPTIMIZE; + + col = _tcolumns; + row = _tlines; + if(row >= 0) + row--; + + ttgetwinsz(&row, &col); + term.t_nrow = (short) row; + term.t_ncol = (short) col; + + if(_cleartoeoln != NULL) /* able to use clear to EOL? */ + term_capabilities |= TT_EOLEXIST; + else + term_capabilities &= ~TT_EOLEXIST; + + if(_setinverse != NULL) + term_capabilities |= TT_REVEXIST; + else + term_capabilities &= ~TT_REVEXIST; + + if(_deletechar == NULL && (_startdelete == NULL || _enddelete == NULL)) + term_capabilities &= ~TT_DELCHAR; + + if(_insertchar == NULL && (_startinsert == NULL || _endinsert == NULL)) + term_capabilities &= ~TT_INSCHAR; + + if((_scrollregion == NULL || _scrolldown == NULL || _scrollup == NULL) + && (_deleteline == NULL || _insertline == NULL)) + term_capabilities &= ~TT_SCROLLEXIST; + + if(_clearscreen == NULL || _moveto == NULL || _up == NULL){ + if(Pmaster == NULL){ + puts("Incomplete termcap entry\n"); + exit(1); + } + } + + ttopen(); + + if(_termcap_init && !Pmaster) { + putpad(_termcap_init); /* any init termcap requires */ + if (_scrollregion) + putpad(tgoto(_scrollregion, term.t_nrow, 0)) ; + } + + /* + * Initialize UW-modified NCSA telnet to use it's functionkeys + */ + if(gmode&MDFKEY && Pmaster == NULL) + puts("\033[99h"); + + /* return ignored */ + return(0); +} + + +static int +tcapclose(void) +{ + if(!Pmaster){ + if(gmode&MDFKEY) + puts("\033[99l"); /* reset UW-NCSA telnet keys */ + + if(_termcap_end) /* any cleanup termcap requires */ + putpad(_termcap_end); + } + + ttclose(); + + /* return ignored */ + return(0); +} + + + +/* + * tcapinsert - insert a character at the current character position. + * _insertchar takes precedence. + */ +static void +tcapinsert(UCS ch) +{ + if(_insertchar != NULL){ + putpad(_insertchar); + ttputc(ch); + } + else{ + putpad(_startinsert); + ttputc(ch); + putpad(_endinsert); + } +} + + +/* + * tcapdelete - delete a character at the current character position. + */ +static void +tcapdelete(void) +{ + if(_startdelete == NULL && _enddelete == NULL) + putpad(_deletechar); + else{ + putpad(_startdelete); + putpad(_deletechar); + putpad(_enddelete); + } +} + + +/* + * o_scrolldown - open a line at the given row position. + * use either region scrolling or deleteline/insertline + * to open a new line. + */ +int +o_scrolldown(int row, int n) +{ + register int i; + + if(_scrollregion != NULL){ + putpad(tgoto(_scrollregion, term.t_nrow - (term.t_mrow+1), row)); + tcapmove(row, 0); + for(i = 0; i < n; i++) + putpad( (_scrollup != NULL && *_scrollup != '\0') + ? _scrollup : "\n" ); + putpad(tgoto(_scrollregion, term.t_nrow, 0)); + tcapmove(row, 0); + } + else{ + /* + * this code causes a jiggly motion of the keymenu when scrolling + */ + for(i = 0; i < n; i++){ + tcapmove(term.t_nrow - (term.t_mrow+1), 0); + putpad(_deleteline); + tcapmove(row, 0); + putpad(_insertline); + } +#ifdef NOWIGGLYLINES + /* + * this code causes a sweeping motion up and down the display + */ + tcapmove(term.t_nrow - term.t_mrow - n, 0); + for(i = 0; i < n; i++) + putpad(_deleteline); + tcapmove(row, 0); + for(i = 0; i < n; i++) + putpad(_insertline); +#endif + } + + /* return ignored */ + return(0); +} + + +/* + * o_scrollup - open a line at the given row position. + * use either region scrolling or deleteline/insertline + * to open a new line. + */ +int +o_scrollup(int row, int n) +{ + register int i; + + if(_scrollregion != NULL){ + putpad(tgoto(_scrollregion, term.t_nrow - (term.t_mrow+1), row)); + /* setting scrolling region moves cursor to home */ + tcapmove(term.t_nrow-(term.t_mrow+1), 0); + for(i = 0;i < n; i++) + putpad((_scrolldown == NULL || _scrolldown[0] == '\0') + ? "\n" : _scrolldown); + putpad(tgoto(_scrollregion, term.t_nrow, 0)); + tcapmove(2, 0); + } + else{ + for(i = 0; i < n; i++){ + tcapmove(row, 0); + putpad(_deleteline); + tcapmove(term.t_nrow - (term.t_mrow+1), 0); + putpad(_insertline); + } +#ifdef NOWIGGLYLINES + /* see note above */ + tcapmove(row, 0); + for(i = 0; i < n; i++) + putpad(_deleteline); + tcapmove(term.t_nrow - term.t_mrow - n, 0); + for(i = 0;i < n; i++) + putpad(_insertline); +#endif + } + + /* return ignored */ + return(0); +} + + +/* + * o_insert - use termcap info to optimized character insert + * returns: true if it optimized output, false otherwise + */ +int +o_insert(UCS c) +{ + if(term_capabilities & TT_INSCHAR){ + tcapinsert(c); + return(1); /* no problems! */ + } + + return(0); /* can't do it. */ +} + + +/* + * o_delete - use termcap info to optimized character insert + * returns true if it optimized output, false otherwise + */ +int +o_delete(void) +{ + if(term_capabilities & TT_DELCHAR){ + tcapdelete(); + return(1); /* deleted, no problem! */ + } + + return(0); /* no dice. */ +} + + +static int +tcapmove(int row, int col) +{ + putpad(tgoto(_moveto, col, row)); + + /* return ignored */ + return(0); +} + + +static int +tcapeeol(void) +{ + int c, starting_col, starting_line; + char *last_bg_color; + + /* + * If the terminal doesn't have back color erase, then we have to + * erase manually to preserve the background color. + */ + if(pico_usingcolor() && (!_bce || !_cleartoeoln)){ + extern int ttcol, ttrow; + + starting_col = ttcol; + starting_line = ttrow; + last_bg_color = pico_get_last_bg_color(); + pico_set_nbg_color(); + for(c = ttcol; c < term.t_ncol; c++) + ttputc(' '); + + tcapmove(starting_line, starting_col); + if(last_bg_color){ + pico_set_bg_color(last_bg_color); + free(last_bg_color); + } + } + else if(_cleartoeoln) + putpad(_cleartoeoln); + + /* return ignored */ + return(0); +} + + +static int +tcapeeop(void) +{ + int i, starting_col, starting_row; + + /* + * If the terminal doesn't have back color erase, then we have to + * erase manually to preserve the background color. + */ + if(pico_usingcolor() && (!_bce || !_cleartoeos)){ + extern int ttcol, ttrow; + + starting_col = ttcol; + starting_row = ttrow; + tcapeeol(); /* rest of this line */ + for(i = ttrow+1; i <= term.t_nrow; i++){ /* the remaining lines */ + tcapmove(i, 0); + tcapeeol(); + } + + tcapmove(starting_row, starting_col); + } + else if(_cleartoeos) + putpad(_cleartoeos); + + /* return ignored */ + return(0); +} + + +static int +tcaprev(int state) /* change reverse video status */ +{ /* FALSE = normal video, TRUE = reverse video */ + if(state) + StartInverse(); + else + EndInverse(); + + return(1); +} + + +static int +tcapbeep(void) +{ + ttputc(BELL); + + /* return ignored */ + return(0); +} + + +void +putpad(char *str) +{ + tputs(str, 1, putchar); +} + +#else /* HARD_CODED_ANSI_TERMINAL */ + +/* + * ANSI-specific terminal i/o and control routines + */ + + +#define BEL 0x07 /* BEL character. */ +#define ESC 0x1B /* ESC character. */ + + +extern int ttflush(); + +extern int ansimove(int, int); +extern int ansieeol(void); +extern int ansieeop(void); +extern int ansibeep(void); +extern int ansiparm(int); +extern int ansiopen(void); +extern int ansiterminalinfo(int); +extern int ansirev(int); + +/* + * Standard terminal interface dispatch table. Most of the fields point into + * "termio" code. + */ +#if defined(VAX) && !defined(__ALPHA) +globaldef +#endif + +TERM term = { + NROW-1, + NCOL, + MARGIN, + MROW, + ansiopen, + ansiterminalinfo, + ttclose, + ttgetc, + ttputc, + ttflush, + ansimove, + ansieeol, + ansieeop, + ansibeep, + ansirev +}; + +int +ansimove(int row, int col) +{ + ttputc(ESC); + ttputc('['); + ansiparm(row+1); + ttputc(';'); + ansiparm(col+1); + ttputc('H'); +} + +int +ansieeol(void) +{ + ttputc(ESC); + ttputc('['); + ttputc('K'); +} + +int +ansieeop(void) +{ + ttputc(ESC); + ttputc('['); + ttputc('J'); +} + +int +ansirev(int state) /* change reverse video state */ +{ /* TRUE = reverse, FALSE = normal */ + static int PrevState = 0; + + if(state != PrevState) { + PrevState = state ; + ttputc(ESC); + ttputc('['); + ttputc(state ? '7': '0'); + ttputc('m'); + } +} + +int +ansibeep(void) +{ + ttputc(BEL); + ttflush(); +} + +int +ansiparm(int n) +{ + register int q; + + q = n/10; + if (q != 0) + ansiparm(q); + ttputc((n%10) + '0'); +} + + +int +ansiterminalinfo(int termcap_wins) +{ +#if V7 + register char *cp; + char *getenv(); + + if ((cp = getenv("TERM")) == NULL) { + puts("Shell variable TERM not defined!"); + exit(1); + } + if (strcmp(cp, "vt100") != 0) { + puts("Terminal type not 'vt100'!"); + exit(1); + } +#endif + /* revexist = TRUE; dead code? */ +} + +int +ansiopen(void) +{ + ttopen(); +} + + +#endif /* HARD_CODED_ANSI_TERMINAL */ +#else /* _WINDOWS */ + +/* These are all just noops in Windows */ + +unsigned +tthascap(void) +{ + return(0); +} + +/* + * o_insert - optimize screen insert of char c + */ +int +o_insert(UCS c) +{ + return(0); +} + + +/* + * o_delete - optimized character deletion + */ +int +o_delete(void) +{ + return(0); +} + + + +#endif /* _WINDOWS */ |