cm.c 12.2 KB
Newer Older
Jim Blandy's avatar
Jim Blandy committed
1
/* Cursor motion subroutines for GNU Emacs.
Paul Eggert's avatar
Paul Eggert committed
2
   Copyright (C) 1985, 1995, 2001-2019 Free Software Foundation, Inc.
Jim Blandy's avatar
Jim Blandy committed
3 4 5 6
    based primarily on public domain code written by Chris Torek

This file is part of GNU Emacs.

7
GNU Emacs is free software: you can redistribute it and/or modify
Jim Blandy's avatar
Jim Blandy committed
8
it under the terms of the GNU General Public License as published by
9 10
the Free Software Foundation, either version 3 of the License, or (at
your option) any later version.
Jim Blandy's avatar
Jim Blandy committed
11 12 13 14 15 16 17

GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
18
along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
Jim Blandy's avatar
Jim Blandy committed
19 20


21
#include <config.h>
22 23

#include "lisp.h"
Jim Blandy's avatar
Jim Blandy committed
24
#include "cm.h"
25
#include "sysstdio.h"
26
#include "termchar.h"
27
#include "tparam.h"
28

29
#define	BIG	9999		/* Good on 32-bit hosts.  */
Jim Blandy's avatar
Jim Blandy committed
30 31 32 33

int cost;		/* sums up costs */

/* ARGSUSED */
Andreas Schwab's avatar
Andreas Schwab committed
34
int
35
evalcost (int c)
Jim Blandy's avatar
Jim Blandy committed
36 37
{
  cost++;
38
  return c;
Jim Blandy's avatar
Jim Blandy committed
39 40
}

41
/* The terminal to use for low-level output. */
42
struct tty_display_info *current_tty;
43

Andreas Schwab's avatar
Andreas Schwab committed
44
int
45
cmputc (int c)
Jim Blandy's avatar
Jim Blandy committed
46
{
47
  if (current_tty->termscript)
48 49
    putc_unlocked (c & 0177, current_tty->termscript);
  putc_unlocked (c & 0177, current_tty->output);
50
  return c;
Jim Blandy's avatar
Jim Blandy committed
51 52 53 54 55 56 57 58 59 60 61
}

/* NEXT TWO ARE DONE WITH MACROS */
#if 0
/*
 * Assume the cursor is at row row, column col.  Normally used only after
 * clearing the screen, when the cursor is at (0, 0), but what the heck,
 * let's let the guy put it anywhere.
 */

static
62 63 64
at (tty, row, col) {
  curY (tty) = row;
  curX (tty)  = col;
Jim Blandy's avatar
Jim Blandy committed
65 66 67 68 69 70 71
}

/*
 * Add n columns to the current cursor position.
 */

static
72 73
addcol (tty, n) {
  curX (tty) += n;
Jim Blandy's avatar
Jim Blandy committed
74 75 76 77 78 79 80 81 82

    /*
     * If cursor hit edge of screen, what happened?
     * N.B.: DO NOT!! write past edge of screen.  If you do, you
     * deserve what you get.  Furthermore, on terminals with
     * autowrap (but not magicwrap), don't write in the last column
     * of the last line.
     */

83
  if (curX (tty) == tty->Wcm->cm_cols) {
Jim Blandy's avatar
Jim Blandy committed
84 85 86 87 88 89
	/*
	 * Well, if magicwrap, still there, past the edge of the
	 * screen (!).  If autowrap, on the col 0 of the next line.
	 * Otherwise on last column.
	 */

90
	if (tty->Wcm->cm_magicwrap)
Jim Blandy's avatar
Jim Blandy committed
91
	    ;			/* "limbo" */
92 93 94
	else if (tty->Wcm->cm_autowrap) {
          curX (tty) = 0;
          curY (tty) ++;		/* Beware end of screen! */
Jim Blandy's avatar
Jim Blandy committed
95 96
	}
	else
97
          curX (tty)--;
Jim Blandy's avatar
Jim Blandy committed
98 99 100 101
    }
}
#endif

Karl Heuer's avatar
Karl Heuer committed
102 103 104 105 106 107 108 109 110 111 112
/*
 * Terminals with magicwrap (xn) don't all behave identically.
 * The VT100 leaves the cursor in the last column but will wrap before
 * printing the next character.  I hear that the Concept terminal does
 * the wrap immediately but ignores the next newline it sees.  And some
 * terminals just have buggy firmware, and think that the cursor is still
 * in limbo if we use direct cursor addressing from the phantom column.
 * The only guaranteed safe thing to do is to emit a CRLF immediately
 * after we reach the last column; this takes us to a known state.
 */
void
113
cmcheckmagic (struct tty_display_info *tty)
Karl Heuer's avatar
Karl Heuer committed
114
{
115
  if (curX (tty) == FrameCols (tty))
Karl Heuer's avatar
Karl Heuer committed
116
    {
117
      if (!MagicWrap (tty) || curY (tty) >= FrameRows (tty) - 1)
118
	emacs_abort ();
119
      if (tty->termscript)
120 121
	putc_unlocked ('\r', tty->termscript);
      putc_unlocked ('\r', tty->output);
122
      if (tty->termscript)
123 124
	putc_unlocked ('\n', tty->termscript);
      putc_unlocked ('\n', tty->output);
125 126
      curX (tty) = 0;
      curY (tty)++;
Karl Heuer's avatar
Karl Heuer committed
127 128 129 130
    }
}


Jim Blandy's avatar
Jim Blandy committed
131 132 133 134 135 136
/*
 * (Re)Initialize the cost factors, given the output speed of the terminal
 * in the variable ospeed.  (Note: this holds B300, B9600, etc -- ie stuff
 * out of <sgtty.h>.)
 */

Andreas Schwab's avatar
Andreas Schwab committed
137
void
138
cmcostinit (struct tty_display_info *tty)
Jim Blandy's avatar
Jim Blandy committed
139 140 141 142 143 144
{
    char *p;

#define	COST(x,e)	(x ? (cost = 0, tputs (x, 1, e), cost) : BIG)
#define CMCOST(x,e)	((x == 0) ? BIG : (p = tgoto(x, 0, 0), COST(p ,e)))

145 146 147 148 149 150 151 152
    tty->Wcm->cc_up =	 COST (tty->Wcm->cm_up, evalcost);
    tty->Wcm->cc_down =	 COST (tty->Wcm->cm_down, evalcost);
    tty->Wcm->cc_left =	 COST (tty->Wcm->cm_left, evalcost);
    tty->Wcm->cc_right = COST (tty->Wcm->cm_right, evalcost);
    tty->Wcm->cc_home =	 COST (tty->Wcm->cm_home, evalcost);
    tty->Wcm->cc_cr =	 COST (tty->Wcm->cm_cr, evalcost);
    tty->Wcm->cc_ll =	 COST (tty->Wcm->cm_ll, evalcost);
    tty->Wcm->cc_tab =	 tty->Wcm->cm_tabwidth ? COST (tty->Wcm->cm_tab, evalcost) : BIG;
Jim Blandy's avatar
Jim Blandy committed
153 154 155 156 157 158 159 160 161 162

    /*
     * These last three are actually minimum costs.  When (if) they are
     * candidates for the least-cost motion, the real cost is computed.
     * (Note that "0" is the assumed to generate the minimum cost.
     * While this is not necessarily true, I have yet to see a terminal
     * for which is not; all the terminals that have variable-cost
     * cursor motion seem to take straight numeric values.  --ACT)
     */

163 164 165
    tty->Wcm->cc_abs =  CMCOST (tty->Wcm->cm_abs, evalcost);
    tty->Wcm->cc_habs = CMCOST (tty->Wcm->cm_habs, evalcost);
    tty->Wcm->cc_vabs = CMCOST (tty->Wcm->cm_vabs, evalcost);
Jim Blandy's avatar
Jim Blandy committed
166 167 168 169 170 171 172 173 174 175 176

#undef CMCOST
#undef COST
}

/*
 * 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.
 */

Andreas Schwab's avatar
Andreas Schwab committed
177
static int
178 179
calccost (struct tty_display_info *tty,
          int srcy, int srcx, int dsty, int dstx, int doit)
Jim Blandy's avatar
Jim Blandy committed
180 181 182 183 184 185 186 187 188 189
{
    register int    deltay,
                    deltax,
                    c,
                    totalcost;
    int     ntabs,
            n2tabs,
            tabx,
            tab2x,
            tabcost;
190
    register const char *p;
Jim Blandy's avatar
Jim Blandy committed
191 192 193 194 195

    /* If have just wrapped on a terminal with xn,
       don't believe the cursor position: give up here
       and force use of absolute positioning.  */

196
    if (curX (tty) == tty->Wcm->cm_cols)
Jim Blandy's avatar
Jim Blandy committed
197 198 199 200 201 202
      goto fail;

    totalcost = 0;
    if ((deltay = dsty - srcy) == 0)
	goto x;
    if (deltay < 0)
203
	p = tty->Wcm->cm_up, c = tty->Wcm->cc_up, deltay = -deltay;
Jim Blandy's avatar
Jim Blandy committed
204
    else
205
	p = tty->Wcm->cm_down, c = tty->Wcm->cc_down;
Jim Blandy's avatar
Jim Blandy committed
206 207 208 209 210 211 212
    if (c == BIG) {		/* caint get thar from here */
	if (doit)
	    printf ("OOPS");
	return c;
    }
    totalcost = c * deltay;
    if (doit)
213
      do
214
          emacs_tputs (tty, p, 1, cmputc);
215
      while (--deltay > 0);
216
x:
Jim Blandy's avatar
Jim Blandy committed
217 218 219
    if ((deltax = dstx - srcx) == 0)
	goto done;
    if (deltax < 0) {
220
	p = tty->Wcm->cm_left, c = tty->Wcm->cc_left, deltax = -deltax;
Jim Blandy's avatar
Jim Blandy committed
221 222 223
	goto dodelta;		/* skip all the tab junk */
    }
    /* Tabs (the toughie) */
224
    if (tty->Wcm->cc_tab >= BIG || !tty->Wcm->cm_usetabs)
Jim Blandy's avatar
Jim Blandy committed
225 226
	goto olddelta;		/* forget it! */

227
    /*
Jim Blandy's avatar
Jim Blandy committed
228 229 230 231 232 233 234
     * ntabs is # tabs towards but not past dstx; n2tabs is one more
     * (ie past dstx), but this is only valid if that is not past the
     * right edge of the screen.  We can check that at the same time
     * as we figure out where we would be if we use the tabs (which
     * we will put into tabx (for ntabs) and tab2x (for n2tabs)).
     */

235
    ntabs = (deltax + srcx % tty->Wcm->cm_tabwidth) / tty->Wcm->cm_tabwidth;
Jim Blandy's avatar
Jim Blandy committed
236
    n2tabs = ntabs + 1;
237 238
    tabx = (srcx / tty->Wcm->cm_tabwidth + ntabs) * tty->Wcm->cm_tabwidth;
    tab2x = tabx + tty->Wcm->cm_tabwidth;
Jim Blandy's avatar
Jim Blandy committed
239

240
    if (tab2x >= tty->Wcm->cm_cols)	/* too far (past edge) */
Jim Blandy's avatar
Jim Blandy committed
241 242
	n2tabs = 0;

243
    /*
Jim Blandy's avatar
Jim Blandy committed
244 245 246 247
     * Now set tabcost to the cost for using ntabs, and c to the cost
     * for using n2tabs, then pick the minimum.
     */

Karoly Lorentey's avatar
Karoly Lorentey committed
248
		   /* cost for ntabs           +    cost for right motion */
249
    tabcost = ntabs ? ntabs * tty->Wcm->cc_tab + (dstx - tabx) * tty->Wcm->cc_right
Jim Blandy's avatar
Jim Blandy committed
250 251
		    : BIG;

Karoly Lorentey's avatar
Karoly Lorentey committed
252
		   /* cost for n2tabs          +    cost for left motion */
253
    c = n2tabs  ?    n2tabs * tty->Wcm->cc_tab + (tab2x - dstx) * tty->Wcm->cc_left
Jim Blandy's avatar
Jim Blandy committed
254 255 256 257 258 259 260 261
		: BIG;

    if (c < tabcost)		/* then cheaper to overshoot & back up */
	ntabs = n2tabs, tabcost = c, tabx = tab2x;

    if (tabcost >= BIG)		/* caint use tabs */
	goto newdelta;

262
    /*
Jim Blandy's avatar
Jim Blandy committed
263 264 265
     * See if tabcost is less than just moving right
     */

266
    if (tabcost < (deltax * tty->Wcm->cc_right)) {
Jim Blandy's avatar
Jim Blandy committed
267 268 269
	totalcost += tabcost;	/* use the tabs */
	if (doit)
	    while (--ntabs >= 0)
270
              emacs_tputs (tty, tty->Wcm->cm_tab, 1, cmputc);
Jim Blandy's avatar
Jim Blandy committed
271 272 273
	srcx = tabx;
    }

274
    /*
Jim Blandy's avatar
Jim Blandy committed
275 276 277
     * Now might as well just recompute the delta.
     */

278
newdelta:
Jim Blandy's avatar
Jim Blandy committed
279 280
    if ((deltax = dstx - srcx) == 0)
	goto done;
281
olddelta:
Jim Blandy's avatar
Jim Blandy committed
282
    if (deltax > 0)
283
	p = tty->Wcm->cm_right, c = tty->Wcm->cc_right;
Jim Blandy's avatar
Jim Blandy committed
284
    else
285
	p = tty->Wcm->cm_left, c = tty->Wcm->cc_left, deltax = -deltax;
Jim Blandy's avatar
Jim Blandy committed
286

287
dodelta:
Jim Blandy's avatar
Jim Blandy committed
288 289 290 291 292 293 294 295
    if (c == BIG) {		/* caint get thar from here */
fail:
	if (doit)
	    printf ("OOPS");
	return BIG;
    }
    totalcost += c * deltax;
    if (doit)
296
      do
297
          emacs_tputs (tty, p, 1, cmputc);
298
      while (--deltax > 0);
299
done:
Jim Blandy's avatar
Jim Blandy committed
300 301 302 303
    return totalcost;
}

#if 0
Andreas Schwab's avatar
Andreas Schwab committed
304 305
void
losecursor (void)
Jim Blandy's avatar
Jim Blandy committed
306 307 308 309 310 311 312 313 314 315
{
  curY = -1;
}
#endif

#define	USEREL	0
#define	USEHOME	1
#define	USELL	2
#define	USECR	3

Andreas Schwab's avatar
Andreas Schwab committed
316
void
317
cmgoto (struct tty_display_info *tty, int row, int col)
Jim Blandy's avatar
Jim Blandy committed
318 319 320 321 322 323
{
    int     homecost,
            crcost,
            llcost,
            relcost,
            directcost;
324
    int use UNINIT;
325 326
    char *p;
    const char *dcm;
Jim Blandy's avatar
Jim Blandy committed
327 328

  /* First the degenerate case */
329
    if (row == curY (tty) && col == curX (tty)) /* already there */
Jim Blandy's avatar
Jim Blandy committed
330 331
    return;

332
    if (curY (tty) >= 0 && curX (tty) >= 0)
Jim Blandy's avatar
Jim Blandy committed
333 334 335 336 337 338
    {
      /* 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.
       */

339
      relcost = calccost (tty, curY (tty), curX (tty), row, col, 0);
Jim Blandy's avatar
Jim Blandy committed
340
      use = USEREL;
341
      if ((homecost = tty->Wcm->cc_home) < BIG)
342
          homecost += calccost (tty, 0, 0, row, col, 0);
Jim Blandy's avatar
Jim Blandy committed
343
      if (homecost < relcost)
344
          relcost = homecost, use = USEHOME;
345 346
      if ((llcost = tty->Wcm->cc_ll) < BIG)
          llcost += calccost (tty, tty->Wcm->cm_rows - 1, 0, row, col, 0);
Jim Blandy's avatar
Jim Blandy committed
347
      if (llcost < relcost)
348
          relcost = llcost, use = USELL;
349 350 351 352
      if ((crcost = tty->Wcm->cc_cr) < BIG) {
	  if (tty->Wcm->cm_autolf)
            if (curY (tty) + 1 >= tty->Wcm->cm_rows)
                crcost = BIG;
Jim Blandy's avatar
Jim Blandy committed
353
	      else
354
                crcost += calccost (tty, curY (tty) + 1, 0, row, col, 0);
Jim Blandy's avatar
Jim Blandy committed
355
	  else
356
            crcost += calccost (tty, curY (tty), 0, row, col, 0);
Jim Blandy's avatar
Jim Blandy committed
357 358 359
      }
      if (crcost < relcost)
	  relcost = crcost, use = USECR;
360 361 362 363 364
      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;
Jim Blandy's avatar
Jim Blandy committed
365 366 367 368
    }
  else
    {
      directcost = 0, relcost = 100000;
369
      dcm = tty->Wcm->cm_abs;
Jim Blandy's avatar
Jim Blandy committed
370 371
    }

372
  /*
Jim Blandy's avatar
Jim Blandy committed
373 374 375 376 377 378 379
   * In the following comparison, the = in <= is because when the costs
   * are the same, it looks nicer (I think) to move directly there.
   */
  if (directcost <= relcost)
    {
      /* compute REAL direct cost */
      cost = 0;
Karoly Lorentey's avatar
Karoly Lorentey committed
380 381 382
      p = (dcm == tty->Wcm->cm_habs
           ? tgoto (dcm, row, col)
           : tgoto (dcm, col, row));
383
      emacs_tputs (tty, p, 1, evalcost);
Jim Blandy's avatar
Jim Blandy committed
384 385
      if (cost <= relcost)
	{	/* really is cheaper */
386
	  emacs_tputs (tty, p, 1, cmputc);
387
	  curY (tty) = row, curX (tty) = col;
Jim Blandy's avatar
Jim Blandy committed
388 389 390 391 392 393
	  return;
	}
    }

  switch (use)
    {
394
    case USEHOME:
395 396
      emacs_tputs (tty, tty->Wcm->cm_home, 1, cmputc);
      curY (tty) = 0, curX (tty) = 0;
Jim Blandy's avatar
Jim Blandy committed
397 398
      break;

399
    case USELL:
400 401
      emacs_tputs (tty, tty->Wcm->cm_ll, 1, cmputc);
      curY (tty) = tty->Wcm->cm_rows - 1, curX (tty) = 0;
Jim Blandy's avatar
Jim Blandy committed
402 403
      break;

404
    case USECR:
405 406 407 408
      emacs_tputs (tty, tty->Wcm->cm_cr, 1, cmputc);
      if (tty->Wcm->cm_autolf)
	curY (tty)++;
      curX (tty) = 0;
Jim Blandy's avatar
Jim Blandy committed
409 410 411
      break;
    }

412 413
  (void) calccost (tty, curY (tty), curX (tty), row, col, 1);
  curY (tty) = row, curX (tty) = col;
Jim Blandy's avatar
Jim Blandy committed
414 415 416 417 418 419
}

/* Clear out all terminal info.
   Used before copying into it the info on the actual terminal.
 */

Andreas Schwab's avatar
Andreas Schwab committed
420
void
421
Wcm_clear (struct tty_display_info *tty)
Jim Blandy's avatar
Jim Blandy committed
422
{
423
  memset (tty->Wcm, 0, sizeof (struct cm));
Jim Blandy's avatar
Jim Blandy committed
424 425 426 427 428 429 430 431 432 433 434
  UP = 0;
  BC = 0;
}

/*
 * Initialized stuff
 * Return 0 if can do CM.
 * Return -1 if cannot.
 * Return -2 if size not specified.
 */

Andreas Schwab's avatar
Andreas Schwab committed
435
int
436
Wcm_init (struct tty_display_info *tty)
Jim Blandy's avatar
Jim Blandy committed
437 438
{
#if 0
439
  if (tty->Wcm->cm_abs && !tty->Wcm->cm_ds)
Jim Blandy's avatar
Jim Blandy committed
440 441
    return 0;
#endif
442
  if (tty->Wcm->cm_abs)
Jim Blandy's avatar
Jim Blandy committed
443 444
    return 0;
  /* Require up and left, and, if no absolute, down and right */
445
  if (!tty->Wcm->cm_up || !tty->Wcm->cm_left)
Jim Blandy's avatar
Jim Blandy committed
446
    return - 1;
447
  if (!tty->Wcm->cm_abs && (!tty->Wcm->cm_down || !tty->Wcm->cm_right))
Jim Blandy's avatar
Jim Blandy committed
448 449
    return - 1;
  /* Check that we know the size of the screen.... */
450
  if (tty->Wcm->cm_rows <= 0 || tty->Wcm->cm_cols <= 0)
Jim Blandy's avatar
Jim Blandy committed
451 452 453
    return - 2;
  return 0;
}