Emacs display code on TTY

Jeremy Bryant

(Date: 28 September 2024)

Summary

The display code in Emacs includes many optimizations. In this article I explore some of the comments and code in the Emacs source. There are indeed a lot of detailed comments in this part of the code. We start with a small part related to TTY.

Cursor motion optimization

The cursor motion subroutines are in cm.c, 458 lines, one of the more tractable parts.

I presume that the TTY code set up to detect low baud rates still applies on modern machines in a different context where the effective latency is low. For example if you are using Emacs over an intermittent mobile connection, through ssh, and tmux or GNU Screen for example. (or a sysadmin in a datacentre using jump/bastion hosts, etc...)

We pick two functions to examine. The function cmgoto moves the cursor to row and col, computing the optimal way of doing so. It calls several times the subroutine calccost which actually computes the cost of moving the cursor, and can optionally move it.

/*
 * Calculate the cost to move from (srcy, srcx) to (dsty, dstx) using
 * up and down, and left and right, motions, and tabs.  If doit is set
 * actually perform the motion.
 */

static int
calccost (struct tty_display_info *tty,
          int srcy, int srcx, int dsty, int dstx, int doit)

Here is a subset where we calculate the delta on the x axis (erow)

x:
    if ((deltax = dstx - srcx) == 0)
	goto done;
    if (deltax < 0) {
	p = tty->Wcm->cm_left, c = tty->Wcm->cc_left, deltax = -deltax;
	goto dodelta;		/* skip all the tab junk */
    }

The cost is calculated as per the specific characteristics of the TTY, from the cost of each character motion such as up or tab. cc_left is the cost of the cm_left operation.

At the end c is the cost, p is the input to emacs_tputs.

We now turn to cmgoto.

void
cmgoto (struct tty_display_info *tty, int row, int col)

Here is a detailed case-by-case walkthrough of the process for looking for optimisation. For example, you can see the HOME case for start-of-line.

    if (curY (tty) >= 0 && curX (tty) >= 0)
    {
      /* We may have quick ways to go to the upper-left, bottom-left,
       * start-of-line, or start-of-next-line.  Or it might be best to
       * start where we are.  Examine the options, and pick the cheapest.
       */

      relcost = calccost (tty, curY (tty), curX (tty), row, col, 0);
      use = USEREL;
      if ((homecost = tty->Wcm->cc_home) < BIG)
          homecost += calccost (tty, 0, 0, row, col, 0);
      if (homecost < relcost)
          relcost = homecost, use = USEHOME;
      if ((llcost = tty->Wcm->cc_ll) < BIG)
          llcost += calccost (tty, tty->Wcm->cm_rows - 1, 0, row, col, 0);
      if (llcost < relcost)
          relcost = llcost, use = USELL;
      if ((crcost = tty->Wcm->cc_cr) < BIG) {
	  if (tty->Wcm->cm_autolf)
            if (curY (tty) + 1 >= tty->Wcm->cm_rows)
                crcost = BIG;
	      else
                crcost += calccost (tty, curY (tty) + 1, 0, row, col, 0);
	  else
            crcost += calccost (tty, curY (tty), 0, row, col, 0);
      }
      if (crcost < relcost)
	  relcost = crcost, use = USECR;
      directcost = tty->Wcm->cc_abs, dcm = tty->Wcm->cm_abs;
      if (row == curY (tty) && tty->Wcm->cc_habs < BIG)
	  directcost = tty->Wcm->cc_habs, dcm = tty->Wcm->cm_habs;
      else if (col == curX (tty) && tty->Wcm->cc_vabs < BIG)
	  directcost = tty->Wcm->cc_vabs, dcm = tty->Wcm->cm_vabs;
    }

Conclusion

The display code of Emacs is an interesting, well-commented and essential part of Emacs. In a future article we will explore other facets of it.