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

This file is part of GNU Emacs.

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

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
along with GNU Emacs; see the file COPYING.  If not, write to
19 20
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */
Jim Blandy's avatar
Jim Blandy committed
21 22


23
#include <config.h>
Jim Blandy's avatar
Jim Blandy committed
24
#include <stdio.h>
25 26 27 28 29

/* For CURTTY */
#include "lisp.h"
#include "frame.h"

Jim Blandy's avatar
Jim Blandy committed
30 31
#include "cm.h"
#include "termhooks.h"
32 33 34
#include "systty.h" /* For emacs_tty in termchar.h */
#include "termchar.h"

Jim Blandy's avatar
Jim Blandy committed
35

36 37 38 39 40
/* For now, don't try to include termcap.h.  On some systems,
   configure finds a non-standard termcap.h that the main build
   won't find.  */

#if defined HAVE_TERMCAP_H && 0
41
#include <termcap.h>
42 43 44
#else
extern void tputs P_ ((const char *, int, int (*)(int)));
extern char *tgoto P_ ((const char *, int, int));
45 46
#endif

Jim Blandy's avatar
Jim Blandy committed
47 48 49 50 51
#define	BIG	9999		/* 9999 good on VAXen.  For 16 bit machines
				   use about 2000.... */

extern char *BC, *UP;

52

Jim Blandy's avatar
Jim Blandy committed
53 54 55
int cost;		/* sums up costs */

/* ARGSUSED */
Andreas Schwab's avatar
Andreas Schwab committed
56
int
Jim Blandy's avatar
Jim Blandy committed
57 58 59 60
evalcost (c)
     char c;
{
  cost++;
61
  return c;
Jim Blandy's avatar
Jim Blandy committed
62 63
}

64 65 66
/* The terminal to use for low-level output. */
struct tty_output * current_tty;

Andreas Schwab's avatar
Andreas Schwab committed
67
int
Jim Blandy's avatar
Jim Blandy committed
68 69 70
cmputc (c)
     char c;
{
71 72 73
  if (TTY_TERMSCRIPT (current_tty))
    putc (c & 0177, TTY_TERMSCRIPT (current_tty));
  putc (c & 0177, TTY_OUTPUT (current_tty));
74
  return c;
Jim Blandy's avatar
Jim Blandy committed
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
}

/* 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
at (row, col) {
    curY = row;
    curX = col;
}

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

static
addcol (n) {
    curX += n;

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

    if (curX == Wcm.cm_cols) {
	/*
	 * 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.
	 */

	if (Wcm.cm_magicwrap)
	    ;			/* "limbo" */
	else if (Wcm.cm_autowrap) {
	    curX = 0;
	    curY++;		/* Beware end of screen! */
	}
	else
	    curX--;
    }
}
#endif

Karl Heuer's avatar
Karl Heuer committed
126 127 128 129 130 131 132 133 134 135 136
/*
 * 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
137 138
cmcheckmagic (tty)
     struct tty_output *tty;
Karl Heuer's avatar
Karl Heuer committed
139
{
140
  if (curX (tty) == FrameCols (tty))
Karl Heuer's avatar
Karl Heuer committed
141
    {
142
      if (!MagicWrap (tty) || curY (tty) >= FrameRows (tty) - 1)
Karl Heuer's avatar
Karl Heuer committed
143
	abort ();
144 145 146 147 148 149
      if (TTY_TERMSCRIPT (tty))
	putc ('\r', TTY_TERMSCRIPT (tty));
      putc ('\r', TTY_OUTPUT (tty));
      if (TTY_TERMSCRIPT (tty))
	putc ('\n', TTY_TERMSCRIPT (tty));
      putc ('\n', TTY_OUTPUT (tty));
150 151
      curX (tty) = 0;
      curY (tty)++;
Karl Heuer's avatar
Karl Heuer committed
152 153 154 155
    }
}


Jim Blandy's avatar
Jim Blandy committed
156 157 158 159 160 161
/*
 * (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
162
void
163
cmcostinit (struct tty_output *tty)
Jim Blandy's avatar
Jim Blandy committed
164 165 166 167 168 169
{
    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)))

170 171 172 173 174 175 176 177
    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
178 179 180 181 182 183 184 185 186 187

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

188 189 190
    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
191 192 193 194 195 196 197 198 199 200 201

#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
202
static int
203
calccost (struct tty_output *tty, int srcy, int srcx, int dsty, int dstx, int doit)
Jim Blandy's avatar
Jim Blandy committed
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
{
    register int    deltay,
                    deltax,
                    c,
                    totalcost;
    int     ntabs,
            n2tabs,
            tabx,
            tab2x,
            tabcost;
    register char  *p;

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

220
    if (curX (tty) == tty->Wcm->cm_cols)
Jim Blandy's avatar
Jim Blandy committed
221 222 223 224 225 226
      goto fail;

    totalcost = 0;
    if ((deltay = dsty - srcy) == 0)
	goto x;
    if (deltay < 0)
227
	p = tty->Wcm->cm_up, c = tty->Wcm->cc_up, deltay = -deltay;
Jim Blandy's avatar
Jim Blandy committed
228
    else
229
	p = tty->Wcm->cm_down, c = tty->Wcm->cc_down;
Jim Blandy's avatar
Jim Blandy committed
230 231 232 233 234 235 236 237
    if (c == BIG) {		/* caint get thar from here */
	if (doit)
	    printf ("OOPS");
	return c;
    }
    totalcost = c * deltay;
    if (doit)
	while (--deltay >= 0)
238
          emacs_tputs (tty, p, 1, cmputc);
239
x:
Jim Blandy's avatar
Jim Blandy committed
240 241 242
    if ((deltax = dstx - srcx) == 0)
	goto done;
    if (deltax < 0) {
243
	p = tty->Wcm->cm_left, c = tty->Wcm->cc_left, deltax = -deltax;
Jim Blandy's avatar
Jim Blandy committed
244 245 246
	goto dodelta;		/* skip all the tab junk */
    }
    /* Tabs (the toughie) */
247
    if (tty->Wcm->cc_tab >= BIG || !tty->Wcm->cm_usetabs)
Jim Blandy's avatar
Jim Blandy committed
248 249
	goto olddelta;		/* forget it! */

250
    /*
Jim Blandy's avatar
Jim Blandy committed
251 252 253 254 255 256 257
     * 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)).
     */

258
    ntabs = (deltax + srcx % tty->Wcm->cm_tabwidth) / tty->Wcm->cm_tabwidth;
Jim Blandy's avatar
Jim Blandy committed
259
    n2tabs = ntabs + 1;
260 261
    tabx = (srcx / tty->Wcm->cm_tabwidth + ntabs) * tty->Wcm->cm_tabwidth;
    tab2x = tabx + tty->Wcm->cm_tabwidth;
Jim Blandy's avatar
Jim Blandy committed
262

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

266
    /*
Jim Blandy's avatar
Jim Blandy committed
267 268 269 270 271
     * Now set tabcost to the cost for using ntabs, and c to the cost
     * for using n2tabs, then pick the minimum.
     */

		   /* cost for ntabs     +    cost for right motion */
272
    tabcost = ntabs ? ntabs * tty->Wcm->cc_tab + (dstx - tabx) * tty->Wcm->cc_right
Jim Blandy's avatar
Jim Blandy committed
273 274 275
		    : BIG;

		   /* cost for n2tabs    +    cost for left motion */
276
    c = n2tabs  ?    n2tabs * tty->Wcm->cc_tab + (tab2x - dstx) * tty->Wcm->cc_left
Jim Blandy's avatar
Jim Blandy committed
277 278 279 280 281 282 283 284
		: BIG;

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

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

285
    /*
Jim Blandy's avatar
Jim Blandy committed
286 287 288
     * See if tabcost is less than just moving right
     */

289
    if (tabcost < (deltax * tty->Wcm->cc_right)) {
Jim Blandy's avatar
Jim Blandy committed
290 291 292
	totalcost += tabcost;	/* use the tabs */
	if (doit)
	    while (--ntabs >= 0)
293
              emacs_tputs (tty, tty->Wcm->cm_tab, 1, cmputc);
Jim Blandy's avatar
Jim Blandy committed
294 295 296
	srcx = tabx;
    }

297
    /*
Jim Blandy's avatar
Jim Blandy committed
298 299 300
     * Now might as well just recompute the delta.
     */

301
newdelta:
Jim Blandy's avatar
Jim Blandy committed
302 303
    if ((deltax = dstx - srcx) == 0)
	goto done;
304
olddelta:
Jim Blandy's avatar
Jim Blandy committed
305
    if (deltax > 0)
306
	p = tty->Wcm->cm_right, c = tty->Wcm->cc_right;
Jim Blandy's avatar
Jim Blandy committed
307
    else
308
	p = tty->Wcm->cm_left, c = tty->Wcm->cc_left, deltax = -deltax;
Jim Blandy's avatar
Jim Blandy committed
309

310
dodelta:
Jim Blandy's avatar
Jim Blandy committed
311 312 313 314 315 316 317 318 319
    if (c == BIG) {		/* caint get thar from here */
fail:
	if (doit)
	    printf ("OOPS");
	return BIG;
    }
    totalcost += c * deltax;
    if (doit)
	while (--deltax >= 0)
320
          emacs_tputs (tty, p, 1, cmputc);
321
done:
Jim Blandy's avatar
Jim Blandy committed
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336
    return totalcost;
}

#if 0
losecursor ()
{
  curY = -1;
}
#endif

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

Andreas Schwab's avatar
Andreas Schwab committed
337
void
338 339
cmgoto (tty, row, col)
     struct tty_output *tty;
340
     int row, col;
Jim Blandy's avatar
Jim Blandy committed
341 342 343 344 345 346 347 348 349 350 351
{
    int     homecost,
            crcost,
            llcost,
            relcost,
            directcost;
    int     use;
    char   *p,
           *dcm;

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

355
    if (curY (tty) >= 0 && curX (tty) >= 0)
Jim Blandy's avatar
Jim Blandy committed
356 357 358 359 360 361
    {
      /* 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.
       */

362
      relcost = calccost (tty, curY (tty), curX (tty), row, col, 0);
Jim Blandy's avatar
Jim Blandy committed
363
      use = USEREL;
364
      if ((homecost = tty->Wcm->cc_home) < BIG)
365
          homecost += calccost (tty, 0, 0, row, col, 0);
Jim Blandy's avatar
Jim Blandy committed
366
      if (homecost < relcost)
367
          relcost = homecost, use = USEHOME;
368 369
      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
370
      if (llcost < relcost)
371
          relcost = llcost, use = USELL;
372 373 374 375
      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
376
	      else
377
                crcost += calccost (tty, curY (tty) + 1, 0, row, col, 0);
Jim Blandy's avatar
Jim Blandy committed
378
	  else
379
            crcost += calccost (tty, curY (tty), 0, row, col, 0);
Jim Blandy's avatar
Jim Blandy committed
380 381 382
      }
      if (crcost < relcost)
	  relcost = crcost, use = USECR;
383 384 385 386 387
      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
388 389 390 391
    }
  else
    {
      directcost = 0, relcost = 100000;
392
      dcm = tty->Wcm->cm_abs;
Jim Blandy's avatar
Jim Blandy committed
393 394
    }

395
  /*
Jim Blandy's avatar
Jim Blandy committed
396 397 398 399 400 401 402
   * 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;
403 404 405
      p = dcm == tty->Wcm->cm_habs
        ? tgoto (dcm, row, col)
        : tgoto (dcm, col, row);
406
      emacs_tputs (tty, p, 1, evalcost);
Jim Blandy's avatar
Jim Blandy committed
407 408
      if (cost <= relcost)
	{	/* really is cheaper */
409
	  emacs_tputs (tty, p, 1, cmputc);
410
	  curY (tty) = row, curX (tty) = col;
Jim Blandy's avatar
Jim Blandy committed
411 412 413 414 415 416
	  return;
	}
    }

  switch (use)
    {
417
    case USEHOME:
418 419
      emacs_tputs (tty, tty->Wcm->cm_home, 1, cmputc);
      curY (tty) = 0, curX (tty) = 0;
Jim Blandy's avatar
Jim Blandy committed
420 421
      break;

422
    case USELL:
423 424
      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
425 426
      break;

427
    case USECR:
428 429 430 431
      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
432 433 434
      break;
    }

435 436
  (void) calccost (tty, curY (tty), curX (tty), row, col, 1);
  curY (tty) = row, curX (tty) = col;
Jim Blandy's avatar
Jim Blandy committed
437 438 439 440 441 442
}

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

Andreas Schwab's avatar
Andreas Schwab committed
443
void
444
Wcm_clear (struct tty_output *tty)
Jim Blandy's avatar
Jim Blandy committed
445
{
446
  bzero (tty->Wcm, sizeof (struct cm));
Jim Blandy's avatar
Jim Blandy committed
447 448 449 450 451 452 453 454 455 456 457
  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
458
int
459
Wcm_init (struct tty_output *tty)
Jim Blandy's avatar
Jim Blandy committed
460 461
{
#if 0
462
  if (tty->Wcm->cm_abs && !tty->Wcm->cm_ds)
Jim Blandy's avatar
Jim Blandy committed
463 464
    return 0;
#endif
465
  if (tty->Wcm->cm_abs)
Jim Blandy's avatar
Jim Blandy committed
466 467
    return 0;
  /* Require up and left, and, if no absolute, down and right */
468
  if (!tty->Wcm->cm_up || !tty->Wcm->cm_left)
Jim Blandy's avatar
Jim Blandy committed
469
    return - 1;
470
  if (!tty->Wcm->cm_abs && (!tty->Wcm->cm_down || !tty->Wcm->cm_right))
Jim Blandy's avatar
Jim Blandy committed
471 472
    return - 1;
  /* Check that we know the size of the screen.... */
473
  if (tty->Wcm->cm_rows <= 0 || tty->Wcm->cm_cols <= 0)
Jim Blandy's avatar
Jim Blandy committed
474 475 476
    return - 2;
  return 0;
}
Miles Bader's avatar
Miles Bader committed
477 478 479

/* arch-tag: bcf64c02-00f6-44ef-94b6-c56eab5b3dc4
   (do not change this comment) */