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