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