xref: /minix/lib/libcurses/cr_put.c (revision 0c3ae37f)
1 /*	$NetBSD: cr_put.c,v 1.31 2011/10/03 12:32: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.31 2011/10/03 12:32: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
mvcur(int ly,int lx,int y,int x)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
__mvcur(int ly,int lx,int y,int x,int in_refresh)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
fgoto(in_refresh)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 = tiparm(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
plodput(c)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
plod(cnt,in_refresh)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
tabcol(col,ts)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