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