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