home *** CD-ROM | disk | FTP | other *** search
- /* this file (in principle) contains all the device-dependent code for
- * handling screen movement and reading the keyboard. public routines are:
- * c_pos(r,c), c_erase(), c_eol();
- * chk_char(), read_char(), read_line (buf, max); and
- * byetty().
- * N.B. we assume output may be performed by printf(), putchar() and
- * fputs(stdout). since these are buffered we flush first in read_char().
- */
-
- /* explanation of various conditional #define options:
- * UNIX: uses termcap for screen management.
- * USE_NDELAY: does non-blocking tty reads with fcntl(O_NDELAY); otherwise
- * this is done with ioctl(..,FIONREAD..). Use which ever works on your
- * system.
- * USE_TERMIO: use termio.h instead of older generic sgtty.h.
- * TURBO_C: compiles for Turbo C 2.0. I'm told it works for Lattice and
- * Microsoft too.
- * USE_ANSISYS: default PC cursor control uses direct BIOS calls (thanks to
- * Mr. Doug McDonald). If your PC does not work with this, however, add
- * "device ANSI.SYS" to your config.sys file and build ephem with
- * USE_ANSISYS.
- * VMS: uses QIO for input, TERMTABLE info for output. This code uses only
- * standard VMS calls, i.e. it does not rely on any third-vendor termcap
- * package or the like. The code includes recoqnition of arrow keys, it
- * is easy to extend it to recoqnize other function keys. also, you don't
- * really need to #define VMS since it is inherent in the compiler.
- * (thanks to Mr. Karsten Spang, NBI, Copenhagen, spang@nbivax.nbi.dk)
- */
-
- /* define one of these... */
- #define UNIX
- /* #define VMS */
- /* #define TURBO_C */
-
- /* then if you defined UNIX you want this too if you don't have FIONREAD */
- /* #define USE_NDELAY */
-
- /* and then if you defined UNIX you want this too if using sgtty.h won't compile
- * for you.
- */
- /* #define USE_TERMIO */
-
- /* if you defined TURBO_C you might want this too if screen io looks garbled */
- /* #define USE_ANSISYS */
-
- #include <stdio.h>
- #include "screen.h"
-
- #ifdef UNIX
- #include <signal.h>
- #ifdef USE_TERMIO
- #include <termio.h>
- #else
- #include <sgtty.h>
- #endif
- #ifdef USE_NDELAY
- #include <fcntl.h>
- #endif
-
- extern char *tgoto();
- static char *cm, *ce, *cl, *kl, *kr, *ku, *kd; /* curses sequences */
- static int tloaded;
- static int ttysetup;
- #ifdef USE_TERMIO
- static struct termio orig_tio;
- #else
- static struct sgttyb orig_sgtty;
- #endif
-
- /* move cursor to row, col, 1-based.
- * we assume this also moves a visible cursor to this location.
- */
- c_pos (r, c)
- int r, c;
- {
- if (!tloaded) tload();
- fputs (tgoto (cm, c-1, r-1), stdout);
- }
-
- /* erase entire screen. */
- c_erase()
- {
- if (!tloaded) tload();
- fputs (cl, stdout);
- }
-
- /* erase to end of line */
- c_eol()
- {
- if (!tloaded) tload();
- fputs (ce, stdout);
- }
-
- #ifdef USE_NDELAY
- static char sav_char; /* one character read-ahead for chk_char() */
- #endif
-
- /* return 0 if there is a char that may be read without blocking, else -1 */
- chk_char()
- {
- #ifdef USE_NDELAY
- if (!ttysetup) setuptty();
- if (sav_char)
- return (0);
- fcntl (0, F_SETFL, O_NDELAY); /* non-blocking read. FNDELAY on BSD */
- if (read (0, &sav_char, 1) != 1)
- sav_char = 0;
- return (sav_char ? 0 : -1);
- #else
- long n;
- if (!ttysetup) setuptty();
- ioctl (0, FIONREAD, &n);
- return (n > 0 ? 0 : -1);
- #endif
- }
-
- /* read the next char, blocking if necessary, and return it. don't echo.
- * map the arrow keys if we can too into hjkl
- */
- read_char()
- {
- char c;
- if (!ttysetup) setuptty();
- fflush (stdout);
- #ifdef USE_NDELAY
- fcntl (0, F_SETFL, 0); /* blocking read */
- if (sav_char) {
- c = sav_char;
- sav_char = 0;
- } else
- #endif
- read (0, &c, 1);
- c = chk_arrow (c & 0177); /* just ASCII, please */
- return (c);
- }
-
- /* used to time out of a read */
- static got_alrm;
- static
- on_alrm()
- {
- got_alrm = 1;
- }
-
- /* see if c is the first of any of the curses arrow key sequences.
- * if it is, read the rest of the sequence, and return the hjkl code
- * that corresponds.
- * if no match, just return c.
- */
- static
- chk_arrow (c)
- register char c;
- {
- register char *seq;
-
- if (c == *(seq = kl) || c == *(seq = kd) || c == *(seq = ku)
- || c == *(seq = kr)) {
- char seqa[32]; /* maximum arrow escape sequence ever expected */
- unsigned l = strlen(seq);
- seqa[0] = c;
- if (l > 1) {
- extern unsigned alarm();
- /* cautiously read rest of arrow sequence */
- got_alrm = 0;
- (void) signal (SIGALRM, on_alrm);
- alarm(2);
- read (0, seqa+1, l-1);
- alarm(0);
- if (got_alrm)
- return (c);
- }
- seqa[l] = '\0';
- if (strcmp (seqa, kl) == 0)
- return ('h');
- if (strcmp (seqa, kd) == 0)
- return ('j');
- if (strcmp (seqa, ku) == 0)
- return ('k');
- if (strcmp (seqa, kr) == 0)
- return ('l');
- }
- return (c);
- }
-
- /* do whatever might be necessary to get the screen and/or tty back into shape.
- */
- byetty()
- {
- #ifdef USE_TERMIO
- ioctl (0, TCSETA, &orig_tio);
- #else
- ioctl (0, TIOCSETP, &orig_sgtty);
- #endif
- #ifdef USE_NDELAY
- fcntl (0, F_SETFL, 0); /* be sure to go back to blocking read */
- #endif
- ttysetup = 0;
- }
-
- static
- tload()
- {
- extern char *getenv(), *tgetstr();
- extern char *UP, *BC;
- char *egetstr();
- static char tbuf[512];
- char rawtbuf[1024];
- char *tp;
- char *ptr;
-
- if (!(tp = getenv ("TERM"))) {
- printf ("no TERM\n");
- exit(1);
- }
-
- if (!ttysetup) setuptty();
- if (tgetent (rawtbuf, tp) != 1) {
- printf ("Can't find termcap for %s\n", tp);
- exit (1);
- }
- ptr = tbuf;
- ku = egetstr ("ku", &ptr);
- kd = egetstr ("kd", &ptr);
- kl = egetstr ("kl", &ptr);
- kr = egetstr ("kr", &ptr);
- cm = egetstr ("cm", &ptr);
- ce = egetstr ("ce", &ptr);
- cl = egetstr ("cl", &ptr);
- UP = egetstr ("up", &ptr);
- if (!tgetflag ("bs"))
- BC = egetstr ("bc", &ptr);
- tloaded = 1;
- }
-
- /* like tgetstr() but discard curses delay codes, for now anyways */
- static char *
- egetstr (name, sptr)
- char *name;
- char **sptr;
- {
- extern char *tgetstr();
- register char c, *s;
-
- s = tgetstr (name, sptr);
- while (((c = *s) >= '0' && c <= '9') || c == '*')
- s += 1;
- return (s);
- }
-
- /* set up tty for char-by-char read, non-blocking */
- static
- setuptty()
- {
- #ifdef USE_TERMIO
- struct termio tio;
-
- ioctl (0, TCGETA, &orig_tio);
- tio = orig_tio;
- tio.c_iflag &= ~ICRNL; /* leave CR unchanged */
- tio.c_oflag &= ~OPOST; /* no output processing */
- tio.c_lflag &= ~(ICANON|ECHO); /* no input processing, no echo */
- tio.c_cc[VMIN] = 1; /* return after each char */
- tio.c_cc[VTIME] = 0; /* no read timeout */
- ioctl (0, TCSETA, &tio);
- #else
- struct sgttyb sg;
-
- ioctl (0, TIOCGETP, &orig_sgtty);
- sg = orig_sgtty;
- sg.sg_flags &= ~ECHO; /* do our own echoing */
- sg.sg_flags &= ~CRMOD; /* leave CR and LF unchanged */
- sg.sg_flags |= XTABS; /* no tabs with termcap */
- sg.sg_flags |= CBREAK; /* wake up on each char but can still kill */
- ioctl (0, TIOCSETP, &sg);
- #endif
- ttysetup = 1;
- }
- /* end of #ifdef UNIX */
- #endif
-
- #ifdef TURBO_C
- #ifdef USE_ANSISYS
- #define ESC '\033'
- /* position cursor.
- * (ANSI: ESC [ r ; c f) (r/c are numbers given in ASCII digits)
- */
- c_pos (r, c)
- int r, c;
- {
- printf ("%c[%d;%df", ESC, r, c);
- }
-
- /* erase entire screen. (ANSI: ESC [ 2 J) */
- c_erase()
- {
- printf ("%c[2J", ESC);
- }
-
- /* erase to end of line. (ANSI: ESC [ K) */
- c_eol()
- {
- printf ("%c[K", ESC);
- }
- #else
- #include <dos.h>
- union REGS rg;
-
- /* position cursor.
- */
- c_pos (r, c)
- int r, c;
- {
- rg.h.ah = 2;
- rg.h.bh = 0;
- rg.h.dh = r-1;
- rg.h.dl = c-1;
- int86(16,&rg,&rg);
- }
-
- /* erase entire screen. */
- c_erase()
- {
- int cur_cursor, i;
- rg.h.ah = 3;
- rg.h.bh = 0;
- int86(16,&rg,&rg);
- cur_cursor = rg.x.dx;
- for(i = 0; i < 25; i++){
- c_pos(i+1,1);
- rg.h.ah = 10;
- rg.h.bh = 0;
- rg.h.al = 32;
- rg.x.cx = 80;
- int86(16,&rg,&rg);
- }
- rg.h.ah = 2;
- rg.h.bh = 0;
- rg.x.dx = cur_cursor;
- int86(16,&rg,&rg);
-
- }
-
- /* erase to end of line.*/
- c_eol()
- {
- int cur_cursor, i;
- rg.h.ah = 3;
- rg.h.bh = 0;
- int86(16,&rg,&rg);
- cur_cursor = rg.x.dx;
- rg.h.ah = 10;
- rg.h.bh = 0;
- rg.h.al = 32;
- rg.x.cx = 80 - rg.h.dl;
- int86(16,&rg,&rg);
- rg.h.ah = 2;
- rg.h.bh = 0;
- rg.x.dx = cur_cursor;
- int86(16,&rg,&rg);
-
- }
- #endif
-
- /* return 0 if there is a char that may be read without blocking, else -1 */
- chk_char()
- {
- return (kbhit() == 0 ? -1 : 0);
- }
-
- /* read the next char, blocking if necessary, and return it. don't echo.
- * map the arrow keys if we can too into hjkl
- */
- read_char()
- {
- int c;
- fflush (stdout);
- c = getch();
- if (c == 0) {
- /* get scan code; convert to direction hjkl if possible */
- c = getch();
- switch (c) {
- case 0x4b: c = 'h'; break;
- case 0x50: c = 'j'; break;
- case 0x48: c = 'k'; break;
- case 0x4d: c = 'l'; break;
- }
- }
- return (c);
- }
-
- /* do whatever might be necessary to get the screen and/or tty back into shape.
- */
- byetty()
- {
- }
- /* end of #ifdef TURBO_C */
- #endif
-
- #ifdef VMS
- #include <string.h>
- #include <iodef.h>
- #include <descrip.h>
- #include <dvidef.h>
- #include <smgtrmptr.h>
- #include <starlet.h>
- #include <lib$routines.h>
- #include <smg$routines.h>
-
- /* Structured types for use in system calls */
- typedef struct{
- unsigned short status;
- unsigned short count;
- unsigned int info;
- } io_status_block;
- typedef struct{
- unsigned short buffer_length;
- unsigned short item_code;
- void *buffer_address;
- unsigned short *return_length_address;
- unsigned long terminator;
- } item_list;
-
- static unsigned short ttchan = 0; /* channel number for terminal */
- volatile static io_status_block iosb; /* I/O status block for operation */
- /* currently in progress */
- volatile static unsigned char input_buf; /* buffer to recieve input charac-*/
- /* ter when operation completes */
- static void *term_entry; /* pointer to TERMTABLE entry */
- #define MAXCAP 10
- static char ce[MAXCAP]; /* ce and cl capability strings for */
- static char cl[MAXCAP]; /* this terminal type */
-
- /* Declaration of special keys to be recoqnized on input */
- /* Number of special keys defined */
- #define MAXKEY 4
- /* TERMTABLE capability codes for the keys */
- static long capcode[MAXKEY] = {SMG$K_KEY_UP_ARROW,SMG$K_KEY_DOWN_ARROW,
- SMG$K_KEY_RIGHT_ARROW,SMG$K_KEY_LEFT_ARROW};
- /* character codes to be returned by read_char when a special key is presssed */
- static int retcode[MAXKEY] = {'k','j','l','h'};
- /* the actual capability strings from the key */
- static char keycap[MAXKEY][MAXCAP];
-
- static char special_buffer[MAXCAP]; /* buffer for reading special key */
- static int chars_in_buffer; /* number of characters in buffer */
-
- /* set up the structures for this I/O module */
- inittt()
- {
- unsigned int status; /* system routine return status */
- $DESCRIPTOR(tt,"TT"); /* terminal name */
- item_list itmlst; /* item list for $getdvi obtaining term type */
- unsigned long devtype; /* terminal type returned form $getdvi */
- unsigned short retlen; /* return length from $getdvi */
- unsigned long lenret; /* return length from smg$get_term_data */
- unsigned long maxlen; /* maximum return length */
- unsigned long cap_code;/* capability code */
- #define MAXINIT 20
- char init_string[MAXINIT];/* string to initialize terminal */
- int key;
-
- /* Assign a channel to the terminal */
- if (!((status = sys$assign(&tt,&ttchan,0,0))&1)) lib$signal(status);
-
- /* Get terminal type. Note that it is possible to use the same
- * iosb at this stage, because no I/O is initiated yet.
- */
- itmlst.buffer_length = 4;
- itmlst.item_code = DVI$_DEVTYPE;
- itmlst.buffer_address = &devtype;
- itmlst.return_length_address = &retlen;
- itmlst.terminator = 0;
- if (!((status = sys$getdviw(0,ttchan,0,&itmlst,&iosb,0,0,0))&1))
- lib$signal(status);
- if (!(iosb.status&1)) lib$signal(iosb.status);
-
- /* Get the TERMTABLE entry corresponding to the terminal type */
- if (!((status = smg$init_term_table_by_type(&devtype,
- &term_entry))&1)) lib$signal(status);
-
- /* Get the initialisation string and initialize terminal */
- cap_code = SMG$K_INIT_STRING;
- maxlen = MAXINIT - 1;
- if (!((status = smg$get_term_data(&term_entry,&cap_code,&maxlen,
- &lenret,init_string))&1)) lib$signal(status);
- init_string[lenret] = '\0';
- fputs(init_string,stdout);
- fflush(stdout);
-
- /* Get ce and cl capabilities, these are static */
- cap_code = SMG$K_ERASE_TO_END_LINE;
- maxlen = MAXCAP-1;
- if (!((status = smg$get_term_data(&term_entry,&cap_code,&maxlen,
- &lenret,ce))&1)) lib$signal(status);
- ce[lenret] = '\0';
-
- cap_code = SMG$K_ERASE_WHOLE_DISPLAY;
- maxlen = MAXCAP-1;
- if (!((status = smg$get_term_data(&term_entry,&cap_code,&maxlen,
- &lenret,cl))&1)) lib$signal(status);
- cl[lenret] = '\0';
-
- /* Here one could obtain line drawing sequences, please feel free
- to implement it ... */
-
- /* Get special keys to be recoqnized on input */
- for (key = 0;key<MAXKEY;key++)
- {
- maxlen = MAXCAP-1;
- if (!((status = smg$get_term_data(&term_entry,&capcode[key],
- &maxlen,&lenret,keycap[key]))&1)) lib$signal(status);
- keycap[key][lenret] = '\0';
- }
-
- /* Initiate first input operation, NOECHO.
- * NOFILTR allows any character to get through, this makes it
- * possible to implement arrow recoqnition, and also makes
- * DEL and BS get through.
- * We don't wait for the operation to complete.
- * Note that stdout has already been fflush'ed above.
- */
- if (!((status = sys$qio(0,ttchan,
- IO$_READVBLK|IO$M_NOECHO|IO$M_NOFILTR,
- &iosb,0,0,&input_buf,1,0,0,0,0))&1)) lib$signal(status);
-
- /* Initialise special key buffer */
- chars_in_buffer = 0;
- } /* inittt */
-
-
- /* return 0 if there is a char that may be read without blocking, else -1 */
- chk_char()
- {
- if (!ttchan) inittt();
-
- return ( chars_in_buffer != 0 ? 0 :(iosb.status == 0 ? -1 : 0));
- }
-
- /* read the next char, blocking if necessary, and return it. don't echo.
- * map the arrow keys if we can too into hjkl
- */
- read_char()
- {
- unsigned int status;
- int buf;
- int i;
- int found_key;
- int key;
- int this_len;
- int match;
-
- if (!ttchan) inittt();
-
- /* If we attempted to read an special key previously, there are characters
- * left in the buffer, return these before doing more I/O
- */
- if (chars_in_buffer!=0){
- buf = special_buffer[0];
- chars_in_buffer--;
- for (i = 0;i<chars_in_buffer;i++)
- {
- special_buffer[i] = special_buffer[i+1];
- }
- special_buffer[chars_in_buffer] = '\0';
- }
- else {
-
- /* Loop over characters read, the loop is terminated when the
- * characters read so far do not match any of the special keys
- * or when the characters read so far is identical to one of
- * the special keys.
- */
-
- do
- {
- /* Wait for I/O to complete */
- if (!((status = sys$synch(0,&iosb))&1)) lib$signal(status);
- special_buffer[chars_in_buffer] = input_buf;
- chars_in_buffer++;
- special_buffer[chars_in_buffer] = '\0';
-
- /* Initiate next input operation */
- fflush (stdout);
- if (!((status = sys$qio(0,ttchan,
- IO$_READVBLK|IO$M_NOECHO|IO$M_NOFILTR,
- &iosb,0,0,&input_buf,1,0,0,0,0))&1)) lib$signal(status);
-
-
- /* Check for match with all special strings */
- match = 0;
- found_key = MAXKEY;
- for (key = 0;key<MAXKEY;key++)
- {
- this_len = strlen(keycap[key]);
- if (this_len<chars_in_buffer) continue;
- if (!strncmp(keycap[key],special_buffer,chars_in_buffer)){
- match = -1;
- if (this_len == chars_in_buffer){
- found_key = key;
- break;
- }
- }
- }
- }
- while (match && (found_key == MAXKEY));
-
- /* If one of the keys matches the input string, return the
- * corresponding key code
- */
- if (found_key != MAXKEY)
- {
- buf = retcode[found_key];
- chars_in_buffer = 0;
- }
- else /* return first character and store the rest in the buffer */
- {
- buf = special_buffer[0];
- chars_in_buffer--;
- for (i = 0;i<chars_in_buffer;i++)
- {
- special_buffer[i] = special_buffer[i+1];
- }
- }
- special_buffer[chars_in_buffer] = '\0';
- }
- return(buf);
- }
-
- /* do whatever might be necessary to get the screen and/or tty back into shape.
- */
- byetty()
- {
- unsigned int status;
-
- if (ttchan)
- {
- /* There is no string in SMG to send to the terminal when
- * terminating, one could clear the screen, move the cursor to
- * the last line, or whatever. This program clears the screen
- * anyway before calling this routine, so we do nothing.
- */
-
-
-
- /* The following is not really neccessary, it will be done at program
- * termination anyway, but if someone tries to use the I/O routines agai
- n
- * it might prove useful...
- */
- if (!((status = smg$del_term_table())&1)) lib$signal(status);
- if (!((status = sys$dassgn(ttchan))&1)) lib$signal(status);
- /* This also cancels any outstanding I/O on the channel */
- ttchan = 0; /* marks terminal I/O as not initialized */
- }
- }
-
- /* position cursor. */
- c_pos (r, c)
- int r, c;
- {
- unsigned long vector[3]; /* argument vector (position) */
- unsigned long status; /* system service return status */
- long lenret; /* length of returned string */
- long maxlen; /* maximum return length */
- unsigned long capcode; /* capability code */
- char seq[2*MAXCAP]; /* returned string */
-
- if (!ttchan) inittt();
-
- /* Set cursor depends on the position, therefore we have to call
- * get_term_data for each operation
- */
- vector[0] = 2;
- vector[1] = r;
- vector[2] = c;
- capcode = SMG$K_SET_CURSOR_ABS;
- maxlen = 2*MAXCAP-1;
- if (!((status = smg$get_term_data(&term_entry,&capcode,&maxlen,
- &lenret,seq,vector))&1)) lib$signal(status);
- seq[lenret] = '\0';
-
- fputs(seq,stdout);
- }
-
- /* erase entire screen. */
- c_erase()
- {
- if (!ttchan) inittt();
-
- fputs(cl,stdout);
- }
-
- /* erase to end of line. */
- c_eol()
- {
- if (!ttchan) inittt();
-
- fputs(ce,stdout);
- }
- /* end of #ifdef VMS */
- #endif
-
- /* read up to max chars into buf, with cannonization.
- * add trailing '\0' (buf is really max+1 chars long).
- * return count of chars read (not counting '\0').
- * assume cursor is already positioned as desired.
- * if type END when n==0 then return -1.
- */
- read_line (buf, max)
- char buf[];
- int max;
- {
- static char erase[] = "\b \b";
- int n, c;
- int done;
-
- #ifdef UNIX
- if (!ttysetup) setuptty();
- #endif
-
- for (done = 0, n = 0; !done; )
- switch (c = read_char()) { /* does not echo */
- case cntrl('h'): /* backspace or */
- case 0177: /* delete are each char erase */
- if (n > 0) {
- fputs (erase, stdout);
- n -= 1;
- }
- break;
- case cntrl('u'): /* line erase */
- while (n > 0) {
- fputs (erase, stdout);
- n -= 1;
- }
- break;
- case '\r': /* EOL */
- done++;
- break;
- default: /* echo and store, if ok */
- if (n == 0 && c == END)
- return (-1);
- if (n >= max)
- putchar (cntrl('g'));
- else if (c >= ' ') {
- putchar (c);
- buf[n++] = c;
- }
- }
-
- buf[n] = '\0';
- return (n);
- }
-