cm.c 12.6 KB
Newer Older
Jim Blandy's avatar
Jim Blandy committed
1
/* Cursor motion subroutines for GNU Emacs.
2
   Copyright (C) 1985, 1995, 2001, 2002, 2003, 2004,
Glenn Morris's avatar
Glenn Morris committed
3
                 2005, 2006, 2007, 2008, 2009, 2010  Free Software Foundation, Inc.
Jim Blandy's avatar
Jim Blandy committed
4 5 6 7
    based primarily on public domain code written by Chris Torek

This file is part of GNU Emacs.

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

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
19
along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
Jim Blandy's avatar
Jim Blandy committed
20 21


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

#include "lisp.h"
#include "frame.h"
Jim Blandy's avatar
Jim Blandy committed
28 29
#include "cm.h"
#include "termhooks.h"
30 31
#include "termchar.h"

Jim Blandy's avatar
Jim Blandy committed
32

33 34 35
/* 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.  */
36 37
extern void tputs P_ ((const char *, int, int (*)(int)));
extern char *tgoto P_ ((const char *, int, int));
38

Jim Blandy's avatar
Jim Blandy committed
39 40 41 42 43 44 45 46
#define	BIG	9999		/* 9999 good on VAXen.  For 16 bit machines
				   use about 2000.... */

extern char *BC, *UP;

int cost;		/* sums up costs */

/* ARGSUSED */
Andreas Schwab's avatar
Andreas Schwab committed
47
int
Jim Blandy's avatar
Jim Blandy committed
48 49 50 51
evalcost (c)
     char c;
{
  cost++;
52
  return c;
Jim Blandy's avatar
Jim Blandy committed
53 54
}

55
/* The terminal to use for low-level output. */
56
struct tty_display_info *current_tty;
57

Andreas Schwab's avatar
Andreas Schwab committed
58
int
Jim Blandy's avatar
Jim Blandy committed
59 60 61
cmputc (c)
     char c;
{
62 63 64
  if (current_tty->termscript)
    putc (c & 0177, current_tty->termscript);
  putc (c & 0177, current_tty->output);
65
  return c;
Jim Blandy's avatar
Jim Blandy committed
66 67 68 69 70 71 72 73 74 75 76
}

/* 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
77 78 79
at (tty, row, col) {
  curY (tty) = row;
  curX (tty)  = col;
Jim Blandy's avatar
Jim Blandy committed
80 81 82 83 84 85 86
}

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

static
87 88
addcol (tty, n) {
  curX (tty) += n;
Jim Blandy's avatar
Jim Blandy committed
89 90 91 92 93 94 95 96 97

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

98
  if (curX (tty) == tty->Wcm->cm_cols) {
Jim Blandy's avatar
Jim Blandy committed
99 100 101 102 103 104
	/*
	 * 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.
	 */

105
	if (tty->Wcm->cm_magicwrap)
Jim Blandy's avatar
Jim Blandy committed
106
	    ;			/* "limbo" */
107 108 109
	else if (tty->Wcm->cm_autowrap) {
          curX (tty) = 0;
          curY (tty) ++;		/* Beware end of screen! */
Jim Blandy's avatar
Jim Blandy committed
110 111
	}
	else
112
          curX (tty)--;
Jim Blandy's avatar
Jim Blandy committed
113 114 115 116
    }
}
#endif

Karl Heuer's avatar
Karl Heuer committed
117 118 119 120 121 122 123 124 125 126 127
/*
 * 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
128
cmcheckmagic (struct tty_display_info *tty)
Karl Heuer's avatar
Karl Heuer committed
129
{
130
  if (curX (tty) == FrameCols (tty))
Karl Heuer's avatar
Karl Heuer committed
131
    {
132
      if (!MagicWrap (tty) || curY (tty) >= FrameRows (tty) - 1)
Karl Heuer's avatar
Karl Heuer committed
133
	abort ();
134 135 136 137 138 139
      if (tty->termscript)
	putc ('\r', tty->termscript);
      putc ('\r', tty->output);
      if (tty->termscript)
	putc ('\n', tty->termscript);
      putc ('\n', tty->output);
140 141
      curX (tty) = 0;
      curY (tty)++;
Karl Heuer's avatar
Karl Heuer committed
142 143 144 145
    }
}


Jim Blandy's avatar
Jim Blandy committed
146 147 148 149 150 151
/*
 * (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
152
void
153
cmcostinit (struct tty_display_info *tty)
Jim Blandy's avatar
Jim Blandy committed
154 155 156 157 158 159
{
    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)))

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

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

178 179 180
    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
181 182 183 184 185 186 187 188 189 190 191

#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
192
static int
193 194
calccost (struct tty_display_info *tty,
          int srcy, int srcx, int dsty, int dstx, int doit)
Jim Blandy's avatar
Jim Blandy committed
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
{
    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.  */

211
    if (curX (tty) == tty->Wcm->cm_cols)
Jim Blandy's avatar
Jim Blandy committed
212 213 214 215 216 217
      goto fail;

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

241
    /*
Jim Blandy's avatar
Jim Blandy committed
242 243 244 245 246 247 248
     * 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)).
     */

249
    ntabs = (deltax + srcx % tty->Wcm->cm_tabwidth) / tty->Wcm->cm_tabwidth;
Jim Blandy's avatar
Jim Blandy committed
250
    n2tabs = ntabs + 1;
251 252
    tabx = (srcx / tty->Wcm->cm_tabwidth + ntabs) * tty->Wcm->cm_tabwidth;
    tab2x = tabx + tty->Wcm->cm_tabwidth;
Jim Blandy's avatar
Jim Blandy committed
253

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

257
    /*
Jim Blandy's avatar
Jim Blandy committed
258 259 260 261
     * 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
262
		   /* cost for ntabs           +    cost for right motion */
263
    tabcost = ntabs ? ntabs * tty->Wcm->cc_tab + (dstx - tabx) * tty->Wcm->cc_right
Jim Blandy's avatar
Jim Blandy committed
264 265
		    : BIG;

Karoly Lorentey's avatar
Karoly Lorentey committed
266
		   /* cost for n2tabs          +    cost for left motion */
267
    c = n2tabs  ?    n2tabs * tty->Wcm->cc_tab + (tab2x - dstx) * tty->Wcm->cc_left
Jim Blandy's avatar
Jim Blandy committed
268 269 270 271 272 273 274 275
		: BIG;

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

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

276
    /*
Jim Blandy's avatar
Jim Blandy committed
277 278 279
     * See if tabcost is less than just moving right
     */

280
    if (tabcost < (deltax * tty->Wcm->cc_right)) {
Jim Blandy's avatar
Jim Blandy committed
281 282 283
	totalcost += tabcost;	/* use the tabs */
	if (doit)
	    while (--ntabs >= 0)
284
              emacs_tputs (tty, tty->Wcm->cm_tab, 1, cmputc);
Jim Blandy's avatar
Jim Blandy committed
285 286 287
	srcx = tabx;
    }

288
    /*
Jim Blandy's avatar
Jim Blandy committed
289 290 291
     * Now might as well just recompute the delta.
     */

292
newdelta:
Jim Blandy's avatar
Jim Blandy committed
293 294
    if ((deltax = dstx - srcx) == 0)
	goto done;
295
olddelta:
Jim Blandy's avatar
Jim Blandy committed
296
    if (deltax > 0)
297
	p = tty->Wcm->cm_right, c = tty->Wcm->cc_right;
Jim Blandy's avatar
Jim Blandy committed
298
    else
299
	p = tty->Wcm->cm_left, c = tty->Wcm->cc_left, deltax = -deltax;
Jim Blandy's avatar
Jim Blandy committed
300

301
dodelta:
Jim Blandy's avatar
Jim Blandy committed
302 303 304 305 306 307 308 309 310
    if (c == BIG) {		/* caint get thar from here */
fail:
	if (doit)
	    printf ("OOPS");
	return BIG;
    }
    totalcost += c * deltax;
    if (doit)
	while (--deltax >= 0)
311
          emacs_tputs (tty, p, 1, cmputc);
312
done:
Jim Blandy's avatar
Jim Blandy committed
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327
    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
328
void
329
cmgoto (tty, row, col)
330
     struct tty_display_info *tty;
331
     int row, col;
Jim Blandy's avatar
Jim Blandy committed
332 333 334 335 336 337 338 339 340 341 342
{
    int     homecost,
            crcost,
            llcost,
            relcost,
            directcost;
    int     use;
    char   *p,
           *dcm;

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

346
    if (curY (tty) >= 0 && curX (tty) >= 0)
Jim Blandy's avatar
Jim Blandy committed
347 348 349 350 351 352
    {
      /* 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.
       */

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

386
  /*
Jim Blandy's avatar
Jim Blandy committed
387 388 389 390 391 392 393
   * 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
394 395 396
      p = (dcm == tty->Wcm->cm_habs
           ? tgoto (dcm, row, col)
           : tgoto (dcm, col, row));
397
      emacs_tputs (tty, p, 1, evalcost);
Jim Blandy's avatar
Jim Blandy committed
398 399
      if (cost <= relcost)
	{	/* really is cheaper */
400
	  emacs_tputs (tty, p, 1, cmputc);
401
	  curY (tty) = row, curX (tty) = col;
Jim Blandy's avatar
Jim Blandy committed
402 403 404 405 406 407
	  return;
	}
    }

  switch (use)
    {
408
    case USEHOME:
409 410
      emacs_tputs (tty, tty->Wcm->cm_home, 1, cmputc);
      curY (tty) = 0, curX (tty) = 0;
Jim Blandy's avatar
Jim Blandy committed
411 412
      break;

413
    case USELL:
414 415
      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
416 417
      break;

418
    case USECR:
419 420 421 422
      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
423 424 425
      break;
    }

426 427
  (void) calccost (tty, curY (tty), curX (tty), row, col, 1);
  curY (tty) = row, curX (tty) = col;
Jim Blandy's avatar
Jim Blandy committed
428 429 430 431 432 433
}

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

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

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