cm.c 12.1 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-2020 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

int cost;		/* sums up costs */

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

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

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

/* 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
61 62 63
at (tty, row, col) {
  curY (tty) = row;
  curX (tty)  = col;
Jim Blandy's avatar
Jim Blandy committed
64 65 66 67 68 69 70
}

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

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

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

82
  if (curX (tty) == tty->Wcm->cm_cols) {
Jim Blandy's avatar
Jim Blandy committed
83 84 85 86 87 88
	/*
	 * 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.
	 */

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

Karl Heuer's avatar
Karl Heuer committed
101 102 103 104 105 106 107 108 109 110 111
/*
 * 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
112
cmcheckmagic (struct tty_display_info *tty)
Karl Heuer's avatar
Karl Heuer committed
113
{
114
  if (curX (tty) == FrameCols (tty))
Karl Heuer's avatar
Karl Heuer committed
115
    {
116
      if (!MagicWrap (tty) || curY (tty) >= FrameRows (tty) - 1)
117
	emacs_abort ();
118
      if (tty->termscript)
119 120
	putc ('\r', tty->termscript);
      putc ('\r', tty->output);
121
      if (tty->termscript)
122 123
	putc ('\n', tty->termscript);
      putc ('\n', tty->output);
124 125
      curX (tty) = 0;
      curY (tty)++;
Karl Heuer's avatar
Karl Heuer committed
126 127 128 129
    }
}


Jim Blandy's avatar
Jim Blandy committed
130 131 132 133 134 135
/*
 * (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
136
void
137
cmcostinit (struct tty_display_info *tty)
Jim Blandy's avatar
Jim Blandy committed
138 139 140 141 142 143
{
    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)))

144 145 146 147 148 149 150 151
    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
152 153 154 155 156 157 158 159 160 161

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

162 163 164
    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
165 166 167 168 169 170 171 172 173 174 175

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

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

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

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

226
    /*
Jim Blandy's avatar
Jim Blandy committed
227 228 229 230 231 232 233
     * 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)).
     */

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

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

242
    /*
Jim Blandy's avatar
Jim Blandy committed
243 244 245 246
     * 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
247
		   /* cost for ntabs           +    cost for right motion */
248
    tabcost = ntabs ? ntabs * tty->Wcm->cc_tab + (dstx - tabx) * tty->Wcm->cc_right
Jim Blandy's avatar
Jim Blandy committed
249 250
		    : BIG;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

371
  /*
Jim Blandy's avatar
Jim Blandy committed
372 373 374 375 376 377 378
   * 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
379 380 381
      p = (dcm == tty->Wcm->cm_habs
           ? tgoto (dcm, row, col)
           : tgoto (dcm, col, row));
382
      emacs_tputs (tty, p, 1, evalcost);
Jim Blandy's avatar
Jim Blandy committed
383 384
      if (cost <= relcost)
	{	/* really is cheaper */
385
	  emacs_tputs (tty, p, 1, cmputc);
386
	  curY (tty) = row, curX (tty) = col;
Jim Blandy's avatar
Jim Blandy committed
387 388 389 390 391 392
	  return;
	}
    }

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

398
    case USELL:
399 400
      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
401 402
      break;

403
    case USECR:
404 405 406 407
      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
408 409 410
      break;
    }

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

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

Andreas Schwab's avatar
Andreas Schwab committed
419
void
420
Wcm_clear (struct tty_display_info *tty)
Jim Blandy's avatar
Jim Blandy committed
421
{
422
  memset (tty->Wcm, 0, sizeof (struct cm));
Jim Blandy's avatar
Jim Blandy committed
423 424 425 426 427 428 429 430 431 432 433
  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
434
int
435
Wcm_init (struct tty_display_info *tty)
Jim Blandy's avatar
Jim Blandy committed
436 437
{
#if 0
438
  if (tty->Wcm->cm_abs && !tty->Wcm->cm_ds)
Jim Blandy's avatar
Jim Blandy committed
439 440
    return 0;
#endif
441
  if (tty->Wcm->cm_abs)
Jim Blandy's avatar
Jim Blandy committed
442 443
    return 0;
  /* Require up and left, and, if no absolute, down and right */
444
  if (!tty->Wcm->cm_up || !tty->Wcm->cm_left)
Jim Blandy's avatar
Jim Blandy committed
445
    return - 1;
446
  if (!tty->Wcm->cm_abs && (!tty->Wcm->cm_down || !tty->Wcm->cm_right))
Jim Blandy's avatar
Jim Blandy committed
447 448
    return - 1;
  /* Check that we know the size of the screen.... */
449
  if (tty->Wcm->cm_rows <= 0 || tty->Wcm->cm_cols <= 0)
Jim Blandy's avatar
Jim Blandy committed
450 451 452
    return - 2;
  return 0;
}