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