📄 tty.c
字号:
/* * UNIX Tty and Process interface * Copyright * (C) 1992 Joseph H. Allen * * This file is part of JOE (Joe's Own Editor) */#include "types.h"#ifdef GWINSZ_IN_SYS_IOCTL#ifdef HAVE_SYS_IOCTL_H#include <sys/ioctl.h>#endif#endif#ifdef HAVE_SYS_WAIT_H#include <sys/wait.h>#endif#ifdef HAVE_SYS_PARAM_H#include <sys/param.h>#endif#ifdef HAVE_OPENPTY#ifdef HAVE_PTY_H#include <pty.h>#endif#ifdef HAVE_LIBUTIL_H#include <libutil.h>#endif#endif#ifdef HAVE_LOGIN_TTY#ifdef HAVE_UTMP_H#include <utmp.h>#endif#endifint idleout = 1;#ifdef __amigaos#undef SIGTSTP#endif/* We use the defines in sys/ioctl to determine what type * tty interface the system uses and what type of system * we actually have. */#ifdef HAVE_POSIX_TERMIOS# include <termios.h>#else# ifdef HAVE_SYSV_TERMIO# include <termio.h># include <sys/termio.h># else# include <sgtty.h># endif#endif#ifdef HAVE_SETITIMER#ifdef HAVE_SYS_TIME_H#include <sys/time.h>#endif#endif/* I'm not sure if SCO_UNIX and ISC have __svr4__ defined, but I think they might */#ifdef M_SYS5#ifndef M_XENIX#include <sys/stream.h>#include <sys/ptem.h>#ifndef __svr4__#define __svr4__ 1#endif#endif#endif#ifdef ISC#ifndef __svr4__#define __svr4__ 1#endif#endif#ifdef __svr4__#include <stropts.h>#endif/** Aliased defines **//* O_NDELAY, O_NONBLOCK, and FNDELAY are all synonyms for placing a descriptor * in non-blocking mode; we make whichever one we have look like O_NDELAY */#ifndef O_NDELAY#ifdef O_NONBLOCK#define O_NDELAY O_NONBLOCK#endif#ifdef FNDELAY#define O_NDELAY FNDELAY#endif#endif/* Some systems define this, some don't */#ifndef sigmask#define sigmask(x) (1<<((x)-1))#endif/* Some BSDs don't have TILDE */#ifndef TILDE#define TILDE 0#endif/* Global configuration variables */int noxon = 0; /* Set if ^S/^Q processing should be disabled */int Baud = 0; /* Baud rate from joerc, cmd line or environment *//* The terminal */FILE *termin = NULL;FILE *termout = NULL;/* Original state of tty */#ifdef HAVE_POSIX_TERMIOSstruct termios oldterm;#else /* HAVE_POSIX_TERMIOS */#ifdef HAVE_SYSV_TERMIOstatic struct termio oldterm;#else /* HAVE_SYSV_TERMIO */static struct sgttyb oarg;static struct tchars otarg;static struct ltchars oltarg;#endif /* HAVE_SYSV_TERMIO */#endif /* HAVE_POSIX_TERMIOS *//* Output buffer, index and size */unsigned char *obuf = NULL;int obufp = 0;int obufsiz;/* The baud rate */unsigned baud; /* Bits per second */unsigned long upc; /* Microseconds per character *//* TTY Speed code to baud-rate conversion table (this is dumb- is it really * too much to ask for them to just use an integer for the baud-rate?) */static int speeds[] = { B50, 50, B75, 75, B110, 110, B134, 134, B150, 150, B200, 200, B300, 300, B600, 600, B1200, 1200, B1800, 1800, B2400, 2400, B4800, 4800, B9600, 9600#ifdef EXTA , EXTA, 19200#endif#ifdef EXTB , EXTB, 38400#endif#ifdef B19200 , B19200, 19200#endif#ifdef B38400 , B38400, 38400#endif};/* Input buffer */int have = 0; /* Set if we have pending input */static unsigned char havec; /* Character read in during pending input check */int leave = 0; /* When set, typeahead checking is disabled *//* TTY mode flag. 1 for open, 0 for closed */static int ttymode = 0;/* Signal state flag. 1 for joe, 0 for normal */static int ttysig = 0;/* Stuff for shell windows */static pid_t kbdpid; /* PID of kbd client */static int ackkbd = -1; /* Editor acks keyboard client to this */static int mpxfd; /* Editor reads packets from this fd */static int mpxsfd; /* Clients send packets to this fd */static int nmpx = 0;static int accept = NO_MORE_DATA; /* =-1 if we have last packet */struct packet { MPX *who; int size; int ch; unsigned char data[1024];} pack;MPX asyncs[NPROC];/* Set signals for JOE */void sigjoe(void){ if (ttysig) return; ttysig = 1; joe_set_signal(SIGHUP, ttsig); joe_set_signal(SIGTERM, ttsig); joe_set_signal(SIGABRT, ttsig); joe_set_signal(SIGINT, SIG_IGN); joe_set_signal(SIGPIPE, SIG_IGN);}/* Restore signals for exiting */void signrm(void){ if (!ttysig) return; ttysig = 0; joe_set_signal(SIGABRT, SIG_DFL); joe_set_signal(SIGHUP, SIG_DFL); joe_set_signal(SIGTERM, SIG_DFL); joe_set_signal(SIGINT, SIG_DFL); joe_set_signal(SIGPIPE, SIG_DFL);}/* Open terminal and set signals */void ttopen(void){ sigjoe(); ttopnn();}/* Close terminal and restore signals */void ttclose(void){ ttclsn(); signrm();}static int winched = 0;#ifdef SIGWINCH/* Window size interrupt handler */static RETSIGTYPE winchd(int unused){ ++winched; REINSTALL_SIGHANDLER(SIGWINCH, winchd);}#endif/* Second ticker */int ticked = 0;static RETSIGTYPE dotick(int unused){ ticked = 1;}void tickoff(void){#ifdef HAVE_SETITIMER struct itimerval val; val.it_value.tv_sec = 0; val.it_value.tv_usec = 0; val.it_interval.tv_sec = 0; val.it_interval.tv_usec = 0; setitimer(ITIMER_REAL,&val,NULL);#else alarm(0);#endif}void tickon(void){#ifdef HAVE_SETITIMER struct itimerval val; val.it_interval.tv_sec = 0; val.it_interval.tv_usec = 0; if (auto_scroll) { int tim = auto_trig_time - mnow(); if (tim < 0) tim = 1; tim *= 1000; val.it_value.tv_sec = 0; val.it_value.tv_usec = tim; } else { val.it_value.tv_sec = 1; val.it_value.tv_usec = 0; } ticked = 0; joe_set_signal(SIGALRM, dotick); setitimer(ITIMER_REAL,&val,NULL);#else ticked = 0; joe_set_signal(SIGALRM, dotick); alarm(1);#endif}/* Open terminal */void ttopnn(void){ int x, bbaud;#ifdef HAVE_POSIX_TERMIOS struct termios newterm;#else#ifdef HAVE_SYSV_TERMIO struct termio newterm;#else struct sgttyb arg; struct tchars targ; struct ltchars ltarg;#endif#endif if (!termin) { if (idleout ? (!(termin = stdin) || !(termout = stdout)) : (!(termin = fopen("/dev/tty", "r")) || !(termout = fopen("/dev/tty", "w")))) { fprintf(stderr, (char *)joe_gettext(_("Couldn\'t open /dev/tty\n"))); exit(1); } else {#ifdef SIGWINCH joe_set_signal(SIGWINCH, winchd);#endif } } if (ttymode) return; ttymode = 1; fflush(termout);#ifdef HAVE_POSIX_TERMIOS tcgetattr(fileno(termin), &oldterm); newterm = oldterm; newterm.c_lflag = 0; if (noxon) newterm.c_iflag &= ~(ICRNL | IGNCR | INLCR | IXON | IXOFF); else newterm.c_iflag &= ~(ICRNL | IGNCR | INLCR); newterm.c_oflag = 0; newterm.c_cc[VMIN] = 1; newterm.c_cc[VTIME] = 0; tcsetattr(fileno(termin), TCSADRAIN, &newterm); bbaud = cfgetospeed(&newterm);#else#ifdef HAVE_SYSV_TERMIO ioctl(fileno(termin), TCGETA, &oldterm); newterm = oldterm; newterm.c_lflag = 0; if (noxon) newterm.c_iflag &= ~(ICRNL | IGNCR | INLCR | IXON | IXOFF); else newterm.c_iflag &= ~(ICRNL | IGNCR | INLCR); newterm.c_oflag = 0; newterm.c_cc[VMIN] = 1; newterm.c_cc[VTIME] = 0; ioctl(fileno(termin), TCSETAW, &newterm); bbaud = (newterm.c_cflag & CBAUD);#else ioctl(fileno(termin), TIOCGETP, &arg); ioctl(fileno(termin), TIOCGETC, &targ); ioctl(fileno(termin), TIOCGLTC, <arg); oarg = arg; otarg = targ; oltarg = ltarg; arg.sg_flags = ((arg.sg_flags & ~(ECHO | CRMOD | XTABS | ALLDELAY | TILDE)) | CBREAK); if (noxon) { targ.t_startc = -1; targ.t_stopc = -1; } targ.t_intrc = -1; targ.t_quitc = -1; targ.t_eofc = -1; targ.t_brkc = -1; ltarg.t_suspc = -1; ltarg.t_dsuspc = -1; ltarg.t_rprntc = -1; ltarg.t_flushc = -1; ltarg.t_werasc = -1; ltarg.t_lnextc = -1; ioctl(fileno(termin), TIOCSETN, &arg); ioctl(fileno(termin), TIOCSETC, &targ); ioctl(fileno(termin), TIOCSLTC, <arg); bbaud = arg.sg_ospeed;#endif#endif baud = 9600; upc = 0; for (x = 0; x != 30; x += 2) if (bbaud == speeds[x]) { baud = speeds[x + 1]; break; } if (Baud) baud = Baud; upc = DIVIDEND / baud; if (obuf) joe_free(obuf); if (!(TIMES * upc)) obufsiz = 4096; else { obufsiz = 1000000 / (TIMES * upc); if (obufsiz > 4096) obufsiz = 4096; } if (!obufsiz) obufsiz = 1; obuf = (unsigned char *) joe_malloc(obufsiz);}/* Close terminal */void ttclsn(void){ int oleave; if (ttymode) ttymode = 0; else return; oleave = leave; leave = 1; ttflsh();#ifdef HAVE_POSIX_TERMIOS tcsetattr(fileno(termin), TCSADRAIN, &oldterm);#else#ifdef HAVE_SYSV_TERMIO ioctl(fileno(termin), TCSETAW, &oldterm);#else ioctl(fileno(termin), TIOCSETN, &oarg); ioctl(fileno(termin), TIOCSETC, &otarg); ioctl(fileno(termin), TIOCSLTC, &oltarg);#endif#endif leave = oleave;}/* Timer interrupt handler */static int yep;static RETSIGTYPE dosig(int unused){ yep = 1;}/* FLush output and check for typeahead */#ifdef HAVE_SETITIMER#ifdef SIG_SETMASKstatic void maskit(void){ sigset_t set; sigemptyset(&set); sigaddset(&set, SIGALRM); sigprocmask(SIG_SETMASK, &set, NULL);}static void unmaskit(void){ sigset_t set; sigemptyset(&set); sigprocmask(SIG_SETMASK, &set, NULL);}static void pauseit(void){ sigset_t set; sigemptyset(&set); sigsuspend(&set);}#elsestatic void maskit(void){ sigsetmask(sigmask(SIGALRM));}static void unmaskit(void){ sigsetmask(0);}static void pauseit(void){ sigpause(0);}#endif#endifint ttflsh(void){ /* Flush output */ if (obufp) { unsigned long usec = obufp * upc; /* No. usecs this write should take */#ifdef HAVE_SETITIMER if (usec >= 50000 && baud < 9600) { struct itimerval a, b; a.it_value.tv_sec = usec / 1000000; a.it_value.tv_usec = usec % 1000000; a.it_interval.tv_usec = 0; a.it_interval.tv_sec = 0; alarm(0); joe_set_signal(SIGALRM, dosig); yep = 0; maskit(); setitimer(ITIMER_REAL, &a, &b); joe_write(fileno(termout), obuf, obufp); while (!yep) pauseit(); unmaskit(); } else joe_write(fileno(termout), obuf, obufp);#else joe_write(fileno(termout), obuf, obufp);#ifdef FIORDCHK if (baud < 9600 && usec / 1000) nap(usec / 1000);#endif#endif obufp = 0; } /* Ack previous packet */ if (ackkbd != -1 && accept != NO_MORE_DATA && !have) { unsigned char c = 0; if (pack.who && pack.who->func) joe_write(pack.who->ackfd, &c, 1); else joe_write(ackkbd, &c, 1); accept = NO_MORE_DATA; } /* Check for typeahead or next packet */ if (!have && !leave) { if (ackkbd != -1) { fcntl(mpxfd, F_SETFL, O_NDELAY); if (read(mpxfd, &pack, sizeof(struct packet) - 1024) > 0) { fcntl(mpxfd, F_SETFL, 0); joe_read(mpxfd, pack.data, pack.size); have = 1; accept = pack.ch; } else fcntl(mpxfd, F_SETFL, 0); } else { /* Set terminal input to non-blocking */ fcntl(fileno(termin), F_SETFL, O_NDELAY); /* Try to read */ if (read(fileno(termin), &havec, 1) == 1) have = 1; /* Set terminal back to blocking */ fcntl(fileno(termin), F_SETFL, 0); } } return 0;}/* Read next character from input */void mpxdied(MPX *m);long last_time;int ttgetc(void){ int stat; long new_time; int flg; tickon(); loop: flg = 0; /* Status line clock */ new_time = time(NULL); if (new_time != last_time) { last_time = new_time; dostaupd = 1; ticked = 1; } /* Autoscroller */ if (auto_scroll && mnow() >= auto_trig_time) { do_auto_scroll(); ticked = 1; flg = 1; } ttflsh(); while (winched) { winched = 0; edupd(1); ttflsh(); } if (ticked) { edupd(flg); ttflsh(); tickon();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -