xref: /netbsd-src/lib/libcurses/cr_put.c (revision 466a16a118933bd295a8a104f095714fadf9cf68)
1 /*	$NetBSD: cr_put.c,v 1.27 2008/07/23 13:32:41 tnozaki 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.27 2008/07/23 13:32:41 tnozaki 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[128];
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 (__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 #ifdef DEBUG
172 		__CTRACE(__CTRACE_OUTPUT, "fgoto: cgp=%s\n", cgp);
173 #endif /* DEBUG */
174 		if (outcol != COLS - 1 &&
175 		    plod((int) strlen(cgp), in_refresh) > 0)
176 			plod(0, in_refresh);
177 		else
178 			tputs(cgp, 0, __cputchar);
179 	} else
180 		plod(0, in_refresh);
181 	outline = destline;
182 	outcol = destcol;
183 }
184 /*
185  * Move (slowly) to destination.
186  * Hard thing here is using home cursor on really deficient terminals.
187  * Otherwise just use cursor motions, hacking use of tabs and overtabbing
188  * and backspace.
189  *
190  * XXX this needs to be revisited for wide characters since we may output
191  * XXX more than one byte for a character.
192  */
193 
194 static int plodcnt, plodflg;
195 
196 static int
197 plodput(c)
198 	int	 c;
199 {
200 	if (plodflg)
201 		--plodcnt;
202 	else
203 		__cputchar(c);
204 	return (0);
205 }
206 
207 static int
208 plod(cnt, in_refresh)
209 	int	 cnt, in_refresh;
210 {
211 	int	 i, j, k, soutcol, soutline;
212 
213 #ifdef DEBUG
214 	__CTRACE(__CTRACE_OUTPUT, "plod: cnt=%d, in_refresh=%d\n",
215 	    cnt, in_refresh);
216 #endif /* DEBUG */
217 	plodcnt = plodflg = cnt;
218 	soutcol = outcol;
219 	soutline = outline;
220 	/*
221 	 * Consider homing and moving down/right from there, vs. moving
222 	 * directly with local motions to the right spot.
223 	 */
224 	if (__tc_ho) {
225 		/*
226 		 * i is the cost to home and tab/space to the right to get to
227 		 * the proper column.  This assumes nd space costs 1 char.  So
228 		 * i + destcol is cost of motion with home.
229 		 */
230 		if (__GT)
231 			i = (destcol / HARDTABS) + (destcol % HARDTABS);
232 		else
233 			i = destcol;
234 
235 		/* j is cost to move locally without homing. */
236 		if (destcol >= outcol) {	/* if motion is to the right */
237 			j = destcol / HARDTABS - outcol / HARDTABS;
238 			if (__GT && j)
239 				j += destcol % HARDTABS;
240 			else
241 				j = destcol - outcol;
242 		} else
243 			/* leftward motion only works if we can backspace. */
244 			if (outcol - destcol <= i && (__tc_bs || __tc_bc))
245 				/* Cheaper to backspace. */
246 				i = j = outcol - destcol;
247 			else
248 				/* Impossibly expensive. */
249 				j = i + 1;
250 
251 		/* k is the absolute value of vertical distance. */
252 		k = outline - destline;
253 		if (k < 0)
254 			k = -k;
255 		j += k;
256 
257 		/* Decision.  We may not have a choice if no up. */
258 		if (i + destline < j || (!__tc_up && destline < outline)) {
259 			/*
260 			 * Cheaper to home.  Do it now and pretend it's a
261 			 * regular local motion.
262 			 */
263 			tputs(__tc_ho, 0, plodput);
264 			outcol = outline = 0;
265 		} else
266 			if (__tc_ll) {
267 				/*
268 				 * Quickly consider homing down and moving from
269 				 * there.  Assume cost of ll is 2.
270 				 */
271 				k = (LINES - 1) - destline;
272 				if (i + k + 2 < j && (k <= 0 || __tc_up)) {
273 					tputs(__tc_ll, 0, plodput);
274 					outcol = 0;
275 					outline = LINES - 1;
276 				}
277 			}
278 	} else
279 		/* No home and no up means it's impossible. */
280 		if (!__tc_up && destline < outline)
281 			return (-1);
282 	if (__GT)
283 		i = destcol % HARDTABS + destcol / HARDTABS;
284 	else
285 		i = destcol;
286 #ifdef notdef
287 	if (__tc_bt && outcol > destcol &&
288 	    (j = (((outcol + 7) & ~7) - destcol - 1) >> 3)) {
289 		j *= (k = strlen(__tc_bt));
290 		if ((k += (destcol & 7)) > 4)
291 			j += 8 - (destcol & 7);
292 		else
293 			j += k;
294 	} else
295 #endif
296 		j = outcol - destcol;
297 
298 	/*
299 	 * If we will later need a \n which will turn into a \r\n by the
300 	 * system or the terminal, then don't bother to try to \r.
301 	 */
302 	if ((__NONL || !__pfast) && outline < destline)
303 		goto dontcr;
304 
305 	/*
306 	 * If the terminal will do a \r\n and there isn't room for it, then
307 	 * we can't afford a \r.
308 	 */
309 	if (__tc_nc && outline >= destline)
310 		goto dontcr;
311 
312 	/*
313 	 * If it will be cheaper, or if we can't back up, then send a return
314 	 * preliminarily.
315 	 */
316 	if (j > i + 1 || (outcol > destcol && !__tc_bs && !__tc_bc)) {
317 		/*
318 		 * BUG: this doesn't take the (possibly long) length of cr
319 		 * into account.
320 		 */
321 		if (__tc_cr)
322 			tputs(__tc_cr, 0, plodput);
323 		else
324 			plodput('\r');
325 		if (__tc_nc) {
326 			if (__tc_nl)
327 				tputs(__tc_nl, 0, plodput);
328 			else
329 				plodput('\n');
330 			outline++;
331 		}
332 		outcol = 0;
333 	}
334 dontcr:while (outline < destline) {
335 		outline++;
336 		if (__tc_nl)
337 			tputs(__tc_nl, 0, plodput);
338 		else
339 			plodput('\n');
340 		if (plodcnt < 0)
341 			goto out;
342 		if (__NONL || __pfast == 0)
343 			outcol = 0;
344 	}
345 	if (__tc_bt)
346 		k = (int) strlen(__tc_bt);
347 	while (outcol > destcol) {
348 		if (plodcnt < 0)
349 			goto out;
350 #ifdef notdef
351 		if (__tc_bt && outcol - destcol > k + 4) {
352 			tputs(__tc_bt, 0, plodput);
353 			outcol--;
354 			outcol &= ~7;
355 			continue;
356 		}
357 #endif
358 		outcol--;
359 		if (__tc_bc)
360 			tputs(__tc_bc, 0, plodput);
361 		else
362 			plodput('\b');
363 	}
364 	while (outline > destline) {
365 		outline--;
366 		tputs(__tc_up, 0, plodput);
367 		if (plodcnt < 0)
368 			goto out;
369 	}
370 	if (__GT && destcol - outcol > 1) {
371 		for (;;) {
372 			i = tabcol(outcol, HARDTABS);
373 			if (i > destcol)
374 				break;
375 			if (__tc_ta)
376 				tputs(__tc_ta, 0, plodput);
377 			else
378 				plodput('\t');
379 			outcol = i;
380 		}
381 		if (destcol - outcol > 4 && i < COLS && (__tc_bc || __tc_bs)) {
382 			if (__tc_ta)
383 				tputs(__tc_ta, 0, plodput);
384 			else
385 				plodput('\t');
386 			outcol = i;
387 			while (outcol > destcol) {
388 				outcol--;
389 				if (__tc_bc)
390 					tputs(__tc_bc, 0, plodput);
391 				else
392 					plodput('\b');
393 			}
394 		}
395 	}
396 	while (outcol < destcol) {
397 		/*
398 		 * Move one char to the right.  We don't use nd space because
399 		 * it's better to just print the char we are moving over.
400 		 */
401 		if (in_refresh)
402 			if (plodflg)	/* Avoid a complex calculation. */
403 				plodcnt--;
404 			else {
405 #ifndef HAVE_WCHAR
406 				i = curscr->lines[outline]->line[outcol].ch
407 				    & __CHARTEXT;
408 				if (curscr->lines[outline]->line[outcol].attr
409 				    == curscr->wattr)
410 					__cputchar(i);
411 #else
412 				if ((curscr->lines[outline]->line[outcol].attr
413 				    & WA_ATTRIBUTES)
414 				    == curscr->wattr) {
415 					switch (WCOL(curscr->lines[outline]->line[outcol])) {
416 					case 1:
417 						__cputwchar(curscr->lines[outline]->line[outcol].ch);
418 						__cursesi_putnsp(curscr->lines[outline]->line[outcol].nsp,
419 								outline,
420 								outcol);
421 #ifdef DEBUG
422 						__CTRACE(__CTRACE_OUTPUT,
423 						    "plod: (%d,%d)WCOL(%d), "
424 						    "putwchar(%x)\n",
425 						    outline, outcol,
426 						    WCOL(curscr->lines[outline]->line[outcol]),
427 						    curscr->lines[outline]->line[outcol].ch);
428 #endif /* DEBUG */
429 					/*FALLTHROUGH*/
430 					case 0:
431 						break;
432 					default:
433 						goto nondes;
434 					}
435 				}
436 #endif /* HAVE_WCHAR */
437 				else
438 					goto nondes;
439 			}
440 		else
441 	nondes:	if (__tc_nd)
442 				tputs(__tc_nd, 0, plodput);
443 			else
444 				plodput(' ');
445 		outcol++;
446 		if (plodcnt < 0)
447 			goto out;
448 	}
449 
450 out:	if (plodflg) {
451 		outcol = soutcol;
452 		outline = soutline;
453 	}
454 #ifdef DEBUG
455 	__CTRACE(__CTRACE_OUTPUT, "plod: returns %d\n", plodcnt);
456 #endif /* DEBUG */
457 	return (plodcnt);
458 }
459 /*
460  * Return the column number that results from being in column col and
461  * hitting a tab, where tabs are set every ts columns.  Work right for
462  * the case where col > COLS, even if ts does not divide COLS.
463  */
464 static int
465 tabcol(col, ts)
466 	int	 col, ts;
467 {
468 	int	 offset;
469 
470 	if (col >= COLS) {
471 		offset = COLS * (col / COLS);
472 		col -= offset;
473 	} else
474 		offset = 0;
475 	return (col + ts - (col % ts) + offset);
476 }
477