]> pd.if.org Git - pd_readline/blobdiff - mg/tty.c
Added mg from an OpenBSD mirror site. Many thanks to the OpenBSD team and the mg...
[pd_readline] / mg / tty.c
diff --git a/mg/tty.c b/mg/tty.c
new file mode 100644 (file)
index 0000000..f803cc7
--- /dev/null
+++ b/mg/tty.c
@@ -0,0 +1,447 @@
+/*     $OpenBSD: tty.c,v 1.30 2008/09/15 16:11:35 kjell Exp $  */
+
+/* This file is in the public domain. */
+
+/*
+ * Terminfo display driver
+ *
+ * Terminfo is a terminal information database and routines to describe
+ * terminals on most modern UNIX systems.  Many other systems have adopted
+ * this as a reasonable way to allow for widely varying and ever changing
+ * varieties of terminal types.         This should be used where practical.
+ */
+/*
+ * Known problems: If you have a terminal with no clear to end of screen and
+ * memory of lines below the ones visible on the screen, display will be
+ * wrong in some cases.  I doubt that any such terminal was ever made, but I
+ * thought everyone with delete line would have clear to end of screen too...
+ *
+ * Code for terminals without clear to end of screen and/or clear to end of line
+ * has not been extensively tested.
+ *
+ * Cost calculations are very rough.  Costs of insert/delete line may be far
+ * from the truth.  This is accentuated by display.c not knowing about
+ * multi-line insert/delete.
+ *
+ * Using scrolling region vs insert/delete line should probably be based on cost
+ * rather than the assumption that scrolling region operations look better.
+ */
+
+#include "def.h"
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+
+#include <term.h>
+
+static int      charcost(char *);
+
+static int      cci;
+static int      insdel;        /* Do we have both insert & delete line? */
+static char    *scroll_fwd;    /* How to scroll forward. */
+
+static void     winchhandler(int);
+
+/* ARGSUSED */
+static void
+winchhandler(int sig)
+{
+       winch_flag = 1;
+}
+
+/*
+ * Initialize the terminal when the editor
+ * gets started up.
+ */
+void
+ttinit(void)
+{
+       int errret;
+
+       if (setupterm(NULL, 1, &errret))
+               panic("Terminal setup failed");
+
+       signal(SIGWINCH, winchhandler);
+       signal(SIGCONT, winchhandler);
+       siginterrupt(SIGWINCH, 1);
+
+       scroll_fwd = scroll_forward;
+       if (scroll_fwd == NULL || *scroll_fwd == '\0') {
+               /* this is what GNU Emacs does */
+               scroll_fwd = parm_down_cursor;
+               if (scroll_fwd == NULL || *scroll_fwd == '\0')
+                       scroll_fwd = "\n";
+       }
+
+       if (cursor_address == NULL || cursor_up == NULL)
+               panic("This terminal is too stupid to run mg");
+
+       /* set nrow & ncol */
+       ttresize();
+
+       if (!clr_eol)
+               tceeol = ncol;
+       else
+               tceeol = charcost(clr_eol);
+
+       /* Estimate cost of inserting a line */
+       if (change_scroll_region && scroll_reverse)
+               tcinsl = charcost(change_scroll_region) * 2 +
+                   charcost(scroll_reverse);
+       else if (parm_insert_line)
+               tcinsl = charcost(parm_insert_line);
+       else if (insert_line)
+               tcinsl = charcost(insert_line);
+       else
+               /* make this cost high enough */
+               tcinsl = nrow * ncol;
+
+       /* Estimate cost of deleting a line */
+       if (change_scroll_region)
+               tcdell = charcost(change_scroll_region) * 2 +
+                   charcost(scroll_fwd);
+       else if (parm_delete_line)
+               tcdell = charcost(parm_delete_line);
+       else if (delete_line)
+               tcdell = charcost(delete_line);
+       else
+               /* make this cost high enough */
+               tcdell = nrow * ncol;
+
+       /* Flag to indicate that we can both insert and delete lines */
+       insdel = (insert_line || parm_insert_line) &&
+           (delete_line || parm_delete_line);
+
+       if (enter_ca_mode)
+               /* enter application mode */
+               putpad(enter_ca_mode, 1);
+
+       ttresize();
+}
+
+/*
+ * Re-initialize the terminal when the editor is resumed.
+ * The keypad_xmit doesn't really belong here but...
+ */
+void
+ttreinit(void)
+{
+       /* check if file was modified while we were gone */
+       if (fchecktime(curbp) != TRUE) {
+               curbp->b_flag |= BFDIRTY;
+       }
+
+       if (enter_ca_mode)
+               /* enter application mode */
+               putpad(enter_ca_mode, 1);
+
+       if (keypad_xmit)
+               /* turn on keypad */
+               putpad(keypad_xmit, 1);
+
+       ttresize();
+}
+
+/*
+ * Clean up the terminal, in anticipation of a return to the command
+ * interpreter. This is a no-op on the ANSI display. On the SCALD display,
+ * it sets the window back to half screen scrolling. Perhaps it should
+ * query the display for the increment, and put it back to what it was.
+ */
+void
+tttidy(void)
+{
+#ifdef XKEYS
+       ttykeymaptidy();
+#endif /* XKEYS */
+
+       /* set the term back to normal mode */
+       if (exit_ca_mode)
+               putpad(exit_ca_mode, 1);
+}
+
+/*
+ * Move the cursor to the specified origin 0 row and column position. Try to
+ * optimize out extra moves; redisplay may have left the cursor in the right
+ * location last time!
+ */
+void
+ttmove(int row, int col)
+{
+       if (ttrow != row || ttcol != col) {
+               putpad(tgoto(cursor_address, col, row), 1);
+               ttrow = row;
+               ttcol = col;
+       }
+}
+
+/*
+ * Erase to end of line.
+ */
+void
+tteeol(void)
+{
+       int     i;
+
+       if (clr_eol)
+               putpad(clr_eol, 1);
+       else {
+               i = ncol - ttcol;
+               while (i--)
+                       ttputc(' ');
+               ttrow = ttcol = HUGE;
+       }
+}
+
+/*
+ * Erase to end of page.
+ */
+void
+tteeop(void)
+{
+       int     line;
+
+       if (clr_eos)
+               putpad(clr_eos, nrow - ttrow);
+       else {
+               putpad(clr_eol, 1);
+               if (insdel)
+                       ttdell(ttrow + 1, lines, lines - ttrow - 1);
+               else {
+                       /* do it by hand */
+                       for (line = ttrow + 1; line <= lines; ++line) {
+                               ttmove(line, 0);
+                               tteeol();
+                       }
+               }
+               ttrow = ttcol = HUGE;
+       }
+}
+
+/*
+ * Make a noise.
+ */
+void
+ttbeep(void)
+{
+       putpad(bell, 1);
+       ttflush();
+}
+
+/*
+ * Insert nchunk blank line(s) onto the screen, scrolling the last line on
+ * the screen off the bottom.  Use the scrolling region if possible for a
+ * smoother display.  If there is no scrolling region, use a set of insert
+ * and delete line sequences.
+ */
+void
+ttinsl(int row, int bot, int nchunk)
+{
+       int     i, nl;
+
+       /* Case of one line insert is special. */
+       if (row == bot) {
+               ttmove(row, 0);
+               tteeol();
+               return;
+       }
+       if (change_scroll_region && scroll_reverse) {
+               /* Use scroll region and back index      */
+               nl = bot - row;
+               ttwindow(row, bot);
+               ttmove(row, 0);
+               while (nchunk--)
+                       putpad(scroll_reverse, nl);
+               ttnowindow();
+               return;
+       } else if (insdel) {
+               ttmove(1 + bot - nchunk, 0);
+               nl = nrow - ttrow;
+               if (parm_delete_line)
+                       putpad(tgoto(parm_delete_line, 0, nchunk), nl);
+               else
+                       /* For all lines in the chunk... */
+                       for (i = 0; i < nchunk; i++)
+                               putpad(delete_line, nl);
+               ttmove(row, 0);
+
+               /* ttmove() changes ttrow */
+               nl = nrow - ttrow;
+
+               if (parm_insert_line)
+                       putpad(tgoto(parm_insert_line, 0, nchunk), nl);
+               else
+                       /* For all lines in the chunk */
+                       for (i = 0; i < nchunk; i++)
+                               putpad(insert_line, nl);
+               ttrow = HUGE;
+               ttcol = HUGE;
+       } else
+               panic("ttinsl: Can't insert/delete line");
+}
+
+/*
+ * Delete nchunk line(s) from "row", replacing the bottom line on the
+ * screen with a blank line.  Unless we're using the scrolling region,
+ * this is done with crafty sequences of insert and delete lines.  The
+ * presence of the echo area makes a boundary condition go away.
+ */
+void
+ttdell(int row, int bot, int nchunk)
+{
+       int     i, nl;
+
+       /* One line special cases */
+       if (row == bot) {
+               ttmove(row, 0);
+               tteeol();
+               return;
+       }
+       /* scrolling region */
+       if (change_scroll_region) {
+               nl = bot - row;
+               ttwindow(row, bot);
+               ttmove(bot, 0);
+               while (nchunk--)
+                       putpad(scroll_fwd, nl);
+               ttnowindow();
+       /* else use insert/delete line */
+       } else if (insdel) {
+               ttmove(row, 0);
+               nl = nrow - ttrow;
+               if (parm_delete_line)
+                       putpad(tgoto(parm_delete_line, 0, nchunk), nl);
+               else
+                       /* For all lines in the chunk    */
+                       for (i = 0; i < nchunk; i++)
+                               putpad(delete_line, nl);
+               ttmove(1 + bot - nchunk, 0);
+
+               /* ttmove() changes ttrow */
+               nl = nrow - ttrow;
+               if (parm_insert_line)
+                       putpad(tgoto(parm_insert_line, 0, nchunk), nl);
+               else
+                       /* For all lines in the chunk */
+                       for (i = 0; i < nchunk; i++)
+                               putpad(insert_line, nl);
+               ttrow = HUGE;
+               ttcol = HUGE;
+       } else
+               panic("ttdell: Can't insert/delete line");
+}
+
+/*
+ * This routine sets the scrolling window on the display to go from line
+ * "top" to line "bot" (origin 0, inclusive).  The caller checks for the
+ * pathological 1-line scroll window which doesn't work right and avoids
+ * it.  The "ttrow" and "ttcol" variables are set to a crazy value to
+ * ensure that the next call to "ttmove" does not turn into a no-op (the
+ * window adjustment moves the cursor).
+ */
+void
+ttwindow(int top, int bot)
+{
+       if (change_scroll_region && (tttop != top || ttbot != bot)) {
+               putpad(tgoto(change_scroll_region, bot, top), nrow - ttrow);
+               ttrow = HUGE;   /* Unknown.              */
+               ttcol = HUGE;
+               tttop = top;    /* Remember region.      */
+               ttbot = bot;
+       }
+}
+
+/*
+ * Switch to full screen scroll. This is used by "spawn.c" just before it
+ * suspends the editor and by "display.c" when it is getting ready to
+ * exit.  This function does a full screen scroll by telling the terminal
+ * to set a scrolling region that is lines or nrow rows high, whichever is
+ * larger.  This behavior seems to work right on systems where you can set
+ * your terminal size.
+ */
+void
+ttnowindow(void)
+{
+       if (change_scroll_region) {
+               putpad(tgoto(change_scroll_region,
+                   (nrow > lines ? nrow : lines) - 1, 0), nrow - ttrow);
+               ttrow = HUGE;   /* Unknown.              */
+               ttcol = HUGE;
+               tttop = HUGE;   /* No scroll region.     */
+               ttbot = HUGE;
+       }
+}
+
+/*
+ * Set the current writing color to the specified color. Watch for color
+ * changes that are not going to do anything (the color is already right)
+ * and don't send anything to the display.  The rainbow version does this
+ * in putline.s on a line by line basis, so don't bother sending out the
+ * color shift.
+ */
+void
+ttcolor(int color)
+{
+       if (color != tthue) {
+               if (color == CTEXT)
+                       /* normal video */
+                       putpad(exit_standout_mode, 1);
+               else if (color == CMODE)
+                       /* reverse video */
+                       putpad(enter_standout_mode, 1);
+               /* save the color */
+               tthue = color;
+       }
+}
+
+/*
+ * This routine is called by the "refresh the screen" command to try
+ * to resize the display. Look in "window.c" to see how
+ * the caller deals with a change.
+ *
+ * We use `newrow' and `newcol' so vtresize() know the difference between the
+ * new and old settings.
+ */
+void
+ttresize(void)
+{
+       int newrow = 0, newcol = 0;
+
+#ifdef TIOCGWINSZ
+       struct  winsize winsize;
+
+       if (ioctl(0, TIOCGWINSZ, &winsize) == 0) {
+               newrow = winsize.ws_row;
+               newcol = winsize.ws_col;
+       }
+#endif
+       if ((newrow <= 0 || newcol <= 0) &&
+           ((newrow = lines) <= 0 || (newcol = columns) <= 0)) {
+               newrow = 24;
+               newcol = 80;
+       }
+       if (vtresize(1, newrow, newcol) != TRUE)
+               panic("vtresize failed");
+}
+
+/*
+ * fake char output for charcost()
+ */
+/* ARGSUSED */
+static int
+fakec(int c)
+{
+       cci++;
+       return (0);
+}
+
+/* calculate the cost of doing string s */
+static int
+charcost(char *s)
+{
+       cci = 0;
+
+       tputs(s, nrow, fakec);
+       return (cci);
+}