Home | History | Annotate | Line # | Download | only in libcurses
cr_put.c revision 1.30
      1 /*	$NetBSD: cr_put.c,v 1.30 2010/02/12 10:06:15 roy Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1981, 1993, 1994
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. Neither the name of the University nor the names of its contributors
     16  *    may be used to endorse or promote products derived from this software
     17  *    without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     29  * SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 #ifndef lint
     34 #if 0
     35 static char sccsid[] = "@(#)cr_put.c	8.3 (Berkeley) 5/4/94";
     36 #else
     37 __RCSID("$NetBSD: cr_put.c,v 1.30 2010/02/12 10:06:15 roy Exp $");
     38 #endif
     39 #endif				/* not lint */
     40 
     41 #include <string.h>
     42 
     43 #include "curses.h"
     44 #include "curses_private.h"
     45 
     46 #define	HARDTABS	8
     47 
     48 /*
     49  * Terminal driving and line formatting routines.  Basic motion optimizations
     50  * are done here as well as formatting lines (printing of control characters,
     51  * line numbering and the like).
     52  */
     53 
     54 /* Stub function for the users. */
     55 int
     56 mvcur(int ly, int lx, int y, int x)
     57 {
     58 	return (__mvcur(ly, lx, y, x, 0));
     59 }
     60 
     61 static void fgoto __P((int));
     62 static int plod __P((int, int));
     63 static int plodput __P((int));
     64 static int tabcol __P((int, int));
     65 
     66 static int outcol, outline, destcol, destline;
     67 
     68 /*
     69  * Sync the position of the output cursor.  Most work here is rounding for
     70  * terminal boundaries getting the column position implied by wraparound or
     71  * the lack thereof and rolling up the screen to get destline on the screen.
     72  */
     73 int
     74 __mvcur(int ly, int lx, int y, int x, int in_refresh)
     75 {
     76 #ifdef DEBUG
     77 	__CTRACE(__CTRACE_OUTPUT,
     78 	    "mvcur: moving cursor from (%d, %d) to (%d, %d)\n", ly, lx, y, x);
     79 #endif
     80 	destcol = x;
     81 	destline = y;
     82 	outcol = lx;
     83 	outline = ly;
     84 	fgoto(in_refresh);
     85 	return (OK);
     86 }
     87 
     88 static void
     89 fgoto(in_refresh)
     90 	int	 in_refresh;
     91 {
     92 	int	 c, l;
     93 	char	*cgp;
     94 
     95 #ifdef DEBUG
     96 	__CTRACE(__CTRACE_OUTPUT, "fgoto: in_refresh=%d\n", in_refresh);
     97 #endif /* DEBUG */
     98 	if (destcol >= COLS) {
     99 		destline += destcol / COLS;
    100 		destcol %= COLS;
    101 	}
    102 	if (outcol >= COLS) {
    103 		l = (outcol + 1) / COLS;
    104 		outline += l;
    105 		outcol %= COLS;
    106 		if (auto_left_margin == 0) {
    107 			while (l > 0) {
    108 				if (__pfast) {
    109 					if (carriage_return)
    110 						tputs(carriage_return,
    111 							0, __cputchar);
    112 					else
    113 						__cputchar('\r');
    114 				}
    115 				if (cursor_down)
    116 					tputs(cursor_down, 0, __cputchar);
    117 				else
    118 					__cputchar('\n');
    119 				l--;
    120 			}
    121 			outcol = 0;
    122 		}
    123 		if (outline > LINES - 1) {
    124 			destline -= outline - (LINES - 1);
    125 			outline = LINES - 1;
    126 		}
    127 	}
    128 	if (destline >= LINES) {
    129 		l = destline;
    130 		destline = LINES - 1;
    131 		if (outline < LINES - 1) {
    132 			c = destcol;
    133 			if (__pfast == 0 && !cursor_address)
    134 				destcol = 0;
    135 			fgoto(in_refresh);
    136 			destcol = c;
    137 		}
    138 		while (l >= LINES) {
    139 			/* The following linefeed (or simulation thereof) is
    140 			 * supposed to scroll up the screen, since we are on
    141 			 * the bottom line.  We make the assumption that
    142 			 * linefeed will scroll.  If ns is in the capability
    143 			 * list this won't work.  We should probably have an
    144 			 * sc capability but sf will generally take the place
    145 			 * if it works.
    146 			 *
    147 			 * Superbee glitch: in the middle of the screen have to
    148 			 * use esc B (down) because linefeed screws up in
    149 			 * "Efficient Paging" (what a joke) mode (which is
    150 			 * essential in some SB's because CRLF mode puts
    151 			 * garbage in at end of memory), but you must use
    152 			 * linefeed to scroll since down arrow won't go past
    153 			 * memory end. I turned this off after recieving Paul
    154 			 * Eggert's Superbee description which wins better. */
    155 			if (cursor_down /* && !__tc_xb */ && __pfast)
    156 				tputs(cursor_down, 0, __cputchar);
    157 			else
    158 				__cputchar('\n');
    159 			l--;
    160 			if (__pfast == 0)
    161 				outcol = 0;
    162 		}
    163 	}
    164 	if (destline < outline && !(cursor_address || cursor_up))
    165 		destline = outline;
    166 
    167 	if (cursor_address &&
    168 	    (cgp = t_vparm(NULL, cursor_address, destline, destcol)))
    169 	{
    170 		/*
    171 		 * Need this condition due to inconsistent behavior
    172 		 * of backspace on the last column.
    173 		 */
    174 #ifdef DEBUG
    175 		__CTRACE(__CTRACE_OUTPUT, "fgoto: cgp=%s\n", cgp);
    176 #endif /* DEBUG */
    177 		if (outcol != COLS - 1 &&
    178 		    plod((int) strlen(cgp), in_refresh) > 0)
    179 			plod(0, in_refresh);
    180 		else
    181 			tputs(cgp, 0, __cputchar);
    182 	} else
    183 		plod(0, in_refresh);
    184 	outline = destline;
    185 	outcol = destcol;
    186 }
    187 /*
    188  * Move (slowly) to destination.
    189  * Hard thing here is using home cursor on really deficient terminals.
    190  * Otherwise just use cursor motions, hacking use of tabs and overtabbing
    191  * and backspace.
    192  *
    193  * XXX this needs to be revisited for wide characters since we may output
    194  * XXX more than one byte for a character.
    195  */
    196 
    197 static int plodcnt, plodflg;
    198 
    199 static int
    200 plodput(c)
    201 	int	 c;
    202 {
    203 	if (plodflg)
    204 		--plodcnt;
    205 	else
    206 		__cputchar(c);
    207 	return (0);
    208 }
    209 
    210 static int
    211 plod(cnt, in_refresh)
    212 	int	 cnt, in_refresh;
    213 {
    214 	int	 i, j, k, soutcol, soutline;
    215 
    216 #ifdef DEBUG
    217 	__CTRACE(__CTRACE_OUTPUT, "plod: cnt=%d, in_refresh=%d\n",
    218 	    cnt, in_refresh);
    219 #endif /* DEBUG */
    220 	plodcnt = plodflg = cnt;
    221 	soutcol = outcol;
    222 	soutline = outline;
    223 
    224 	/*
    225 	 * Consider homing and moving down/right from there, vs. moving
    226 	 * directly with local motions to the right spot.
    227 	 */
    228 	if (cursor_home) {
    229 		/*
    230 		 * i is the cost to home and tab/space to the right to get to
    231 		 * the proper column.  This assumes nd space costs 1 char.  So
    232 		 * i + destcol is cost of motion with home.
    233 		 */
    234 		if (__GT)
    235 			i = (destcol / HARDTABS) + (destcol % HARDTABS);
    236 		else
    237 			i = destcol;
    238 
    239 		/* j is cost to move locally without homing. */
    240 		if (destcol >= outcol) {	/* if motion is to the right */
    241 			j = destcol / HARDTABS - outcol / HARDTABS;
    242 			if (__GT && j)
    243 				j += destcol % HARDTABS;
    244 			else
    245 				j = destcol - outcol;
    246 		} else
    247 			/* leftward motion only works if we can backspace. */
    248 			if (outcol - destcol <= i)
    249 				/* Cheaper to backspace. */
    250 				i = j = outcol - destcol;
    251 			else
    252 				/* Impossibly expensive. */
    253 				j = i + 1;
    254 
    255 		/* k is the absolute value of vertical distance. */
    256 		k = outline - destline;
    257 		if (k < 0)
    258 			k = -k;
    259 		j += k;
    260 
    261 		/* Decision.  We may not have a choice if no up. */
    262 		if (i + destline < j || (!cursor_up && destline < outline)) {
    263 			/*
    264 			 * Cheaper to home.  Do it now and pretend it's a
    265 			 * regular local motion.
    266 			 */
    267 			tputs(cursor_home, 0, plodput);
    268 			outcol = outline = 0;
    269 		} else
    270 			if (cursor_to_ll) {
    271 				/*
    272 				 * Quickly consider homing down and moving from
    273 				 * there.  Assume cost of ll is 2.
    274 				 */
    275 				k = (LINES - 1) - destline;
    276 				if (i + k + 2 < j && (k <= 0 || cursor_up)) {
    277 					tputs(cursor_to_ll, 0, plodput);
    278 					outcol = 0;
    279 					outline = LINES - 1;
    280 				}
    281 			}
    282 	} else
    283 		/* No home and no up means it's impossible. */
    284 		if (!cursor_up && destline < outline)
    285 			return (-1);
    286 	if (__GT)
    287 		i = destcol % HARDTABS + destcol / HARDTABS;
    288 	else
    289 		i = destcol;
    290 #ifdef notdef
    291 	if (back_tab && outcol > destcol &&
    292 	    (j = (((outcol + 7) & ~7) - destcol - 1) >> 3)) {
    293 		j *= (k = strlen(back_tab));
    294 		if ((k += (destcol & 7)) > 4)
    295 			j += 8 - (destcol & 7);
    296 		else
    297 			j += k;
    298 	} else
    299 #endif
    300 		j = outcol - destcol;
    301 
    302 	/*
    303 	 * If we will later need a \n which will turn into a \r\n by the
    304 	 * system or the terminal, then don't bother to try to \r.
    305 	 */
    306 	if ((__NONL || !__pfast) && outline < destline)
    307 		goto dontcr;
    308 
    309 	/*
    310 	 * If the terminal will do a \r\n and there isn't room for it, then
    311 	 * we can't afford a \r.
    312 	 */
    313 	if (!carriage_return && outline >= destline)
    314 		goto dontcr;
    315 
    316 	/*
    317 	 * If it will be cheaper, or if we can't back up, then send a return
    318 	 * preliminarily.
    319 	 */
    320 	if (j > i + 1 || outcol > destcol) {
    321 		/*
    322 		 * BUG: this doesn't take the (possibly long) length of cr
    323 		 * into account.
    324 		 */
    325 		if (carriage_return)
    326 			tputs(carriage_return, 0, plodput);
    327 		else
    328 			plodput('\r');
    329 		if (!carriage_return) {
    330 			if (cursor_down)
    331 				tputs(cursor_down, 0, plodput);
    332 			else
    333 				plodput('\n');
    334 			outline++;
    335 		}
    336 		outcol = 0;
    337 	}
    338 dontcr:while (outline < destline) {
    339 		outline++;
    340 		if (cursor_down)
    341 			tputs(cursor_down, 0, plodput);
    342 		else
    343 			plodput('\n');
    344 		if (plodcnt < 0)
    345 			goto out;
    346 		if (__NONL || __pfast == 0)
    347 			outcol = 0;
    348 	}
    349 	if (back_tab)
    350 		k = (int) strlen(back_tab);
    351 	while (outcol > destcol) {
    352 		if (plodcnt < 0)
    353 			goto out;
    354 #ifdef notdef
    355 		if (back_tab && outcol - destcol > k + 4) {
    356 			tputs(back_tab, 0, plodput);
    357 			outcol--;
    358 			outcol &= ~7;
    359 			continue;
    360 		}
    361 #endif
    362 		outcol--;
    363 		if (cursor_left)
    364 			tputs(cursor_left, 0, plodput);
    365 		else
    366 			plodput('\b');
    367 	}
    368 	while (outline > destline) {
    369 		outline--;
    370 		tputs(cursor_up, 0, plodput);
    371 		if (plodcnt < 0)
    372 			goto out;
    373 	}
    374 	if (__GT && destcol - outcol > 1) {
    375 		for (;;) {
    376 			i = tabcol(outcol, HARDTABS);
    377 			if (i > destcol)
    378 				break;
    379 			if (tab)
    380 				tputs(tab, 0, plodput);
    381 			else
    382 				plodput('\t');
    383 			outcol = i;
    384 		}
    385 		if (destcol - outcol > 4 && i < COLS) {
    386 			if (tab)
    387 				tputs(tab, 0, plodput);
    388 			else
    389 				plodput('\t');
    390 			outcol = i;
    391 			while (outcol > destcol) {
    392 				outcol--;
    393 				if (cursor_left)
    394 					tputs(cursor_left, 0, plodput);
    395 				else
    396 					plodput('\b');
    397 			}
    398 		}
    399 	}
    400 	while (outcol < destcol) {
    401 		/*
    402 		 * Move one char to the right.  We don't use nd space because
    403 		 * it's better to just print the char we are moving over.
    404 		 */
    405 		if (in_refresh)
    406 			if (plodflg)	/* Avoid a complex calculation. */
    407 				plodcnt--;
    408 			else {
    409 #ifndef HAVE_WCHAR
    410 				i = curscr->alines[outline]->line[outcol].ch
    411 				    & __CHARTEXT;
    412 				if (curscr->alines[outline]->line[outcol].attr
    413 				    == curscr->wattr)
    414 					__cputchar(i);
    415 #else
    416 				if ((curscr->alines[outline]->line[outcol].attr
    417 				    & WA_ATTRIBUTES)
    418 				    == curscr->wattr) {
    419 					switch (WCOL(curscr->alines[outline]->line[outcol])) {
    420 					case 1:
    421 						__cputwchar(curscr->alines[outline]->line[outcol].ch);
    422 						__cursesi_putnsp(curscr->alines[outline]->line[outcol].nsp,
    423 								outline,
    424 								outcol);
    425 #ifdef DEBUG
    426 						__CTRACE(__CTRACE_OUTPUT,
    427 						    "plod: (%d,%d)WCOL(%d), "
    428 						    "putwchar(%x)\n",
    429 						    outline, outcol,
    430 						    WCOL(curscr->alines[outline]->line[outcol]),
    431 						    curscr->alines[outline]->line[outcol].ch);
    432 #endif /* DEBUG */
    433 					/*FALLTHROUGH*/
    434 					case 0:
    435 						break;
    436 					default:
    437 						goto nondes;
    438 					}
    439 				}
    440 #endif /* HAVE_WCHAR */
    441 				else
    442 					goto nondes;
    443 			}
    444 		else
    445 	nondes:	if (cursor_right)
    446 			tputs(cursor_right, 0, plodput);
    447 		else
    448 			plodput(' ');
    449 		outcol++;
    450 		if (plodcnt < 0)
    451 			goto out;
    452 	}
    453 
    454 out:	if (plodflg) {
    455 		outcol = soutcol;
    456 		outline = soutline;
    457 	}
    458 #ifdef DEBUG
    459 	__CTRACE(__CTRACE_OUTPUT, "plod: returns %d\n", plodcnt);
    460 #endif /* DEBUG */
    461 	return (plodcnt);
    462 }
    463 /*
    464  * Return the column number that results from being in column col and
    465  * hitting a tab, where tabs are set every ts columns.  Work right for
    466  * the case where col > COLS, even if ts does not divide COLS.
    467  */
    468 static int
    469 tabcol(col, ts)
    470 	int	 col, ts;
    471 {
    472 	int	 offset;
    473 
    474 	if (col >= COLS) {
    475 		offset = COLS * (col / COLS);
    476 		col -= offset;
    477 	} else
    478 		offset = 0;
    479 	return (col + ts - (col % ts) + offset);
    480 }
    481