xref: /illumos-gate/usr/src/lib/libxcurses2/src/libc/xcurses/mvcur.c (revision 4de2612967d06c4fdbf524a62556a1e8118a006f)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright (c) 1995-1998 by Sun Microsystems, Inc.
24  * All rights reserved.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /* LINTLIBRARY */
30 
31 /*
32  * mvcur.c
33  *
34  * XCurses Library
35  *
36  * Copyright 1990, 1995 by Mortice Kern Systems Inc. All rights reserved.
37  *
38  */
39 
40 #ifdef M_RCSID
41 #ifndef lint
42 static char rcsID[] =
43 "$Header: /team/ps/sun_xcurses/archive/local_changes/xcurses/src/lib/"
44 "libxcurses/src/libc/xcurses/rcs/mvcur.c 1.4 1998/05/29 18:09:09 "
45 "cbates Exp $";
46 #endif
47 #endif
48 
49 #include <private.h>
50 #include <string.h>
51 
52 #define	VECTOR_SIZE		128	/* size of strategy buffer */
53 
54 /*
55  * #define
56  * Make_seq_best(s1, s2)
57  *
58  * Make_seq_best() swaps the values of the pointers if s1->cost > s2->cost.
59  */
60 #define	Make_seq_best(s1, s2)	\
61 	if (s1->cost > s2->cost) {	\
62 	    struct Sequence	*temp = s1; \
63 	    s1 = s2;			\
64 	    s2 = temp;			\
65 	}
66 
67 #define	zero_seq(seq)	((seq)->end = (seq)->vec, (seq)->cost = 0)
68 
69 struct Sequence {
70 	int	vec[VECTOR_SIZE];	/* vector of operations */
71 	int	*end;		/* end of vector */
72 	int	cost;		/* cost of vector */
73 };
74 
75 static bool	relative;		/* set if we really know where we are */
76 
77 /*
78  * Add sequence 2 to sequence 1.
79  */
80 static void
81 add_seq(struct Sequence *seq1, struct Sequence *seq2)
82 {
83 	if (seq1->cost >= __MOVE_INFINITY || seq2->cost >= __MOVE_INFINITY)
84 		seq1->cost = __MOVE_INFINITY;
85 	else {
86 		int	*vptr = seq2->vec;
87 		while (vptr != seq2->end)
88 			*(seq1->end++) = *(vptr++);
89 		seq1->cost += seq2->cost;
90 	}
91 }
92 
93 /*
94  * add_op() adds the operator op and the appropriate
95  * number of paramaters to seq.  It also increases the
96  * cost appropriately.
97  *
98  * If op takes no parameters then p0 is taken to be a count.
99  */
100 static void
101 add_op(struct Sequence *seq, int op, int p1, int p2)
102 {
103 	*(seq->end++) = op;
104 	*(seq->end++) = p1;
105 	*(seq->end++) = p2;
106 
107 	if (cur_term->_move[op]._seq == NULL) {
108 		seq->cost = __MOVE_INFINITY;
109 	} else if (op < __MOVE_MAX_RELATIVE) {
110 		/* No parameters, total is cost * p1. */
111 		seq->cost += cur_term->_move[op]._cost * p1;
112 	} else {
113 		/* Cursor motion using parameters have fixed cost. */
114 		seq->cost = cur_term->_move[op]._cost;
115 	}
116 }
117 
118 /*
119  * row() adds the best sequence for moving the cursor from orow
120  * to nrow to seq.
121  *
122  * row() considers row_address, parm_up/down_cursor and cursor_up/down.
123  */
124 static void
125 row(struct Sequence *outseq, int orow, int nrow)
126 {
127 	struct Sequence	seqA, seqB;
128 	struct Sequence	*best = &seqA;
129 	struct Sequence	*try = &seqB;
130 	int	parm_cursor, one_step, dist;
131 
132 	if (nrow == orow)
133 		return;
134 
135 	if (nrow < orow) {
136 		parm_cursor = __MOVE_N_UP;
137 		one_step = __MOVE_UP;
138 		dist = orow - nrow;
139 	} else {
140 		parm_cursor = __MOVE_N_DOWN;
141 		one_step = __MOVE_DOWN;
142 		dist = nrow - orow;
143 	}
144 
145 	/* try out direct row addressing */
146 	zero_seq(best);
147 	add_op(best, __MOVE_ROW, nrow, 0);
148 
149 	/* try out paramaterized up or down motion */
150 	zero_seq(try);
151 	add_op(try, parm_cursor, dist, 0);
152 	Make_seq_best(best, try);
153 
154 	/* try getting there one step at a time... */
155 	zero_seq(try);
156 	add_op(try, one_step, dist, 0);
157 	Make_seq_best(best, try);
158 
159 	add_seq(outseq, best);
160 }
161 
162 /*
163  * Motion indexes used in simp_col().
164  */
165 typedef struct {
166 	int	_tab;	/* Tab index. */
167 	int	_one;	/* Single-step index, same direction as tab. */
168 	int	_opp;	/* Single-step index, opposite direction to tab. */
169 } t_steps;
170 
171 /*
172  * simp_col(outseq, oldcol, newcol)
173  *
174  * simp_col() adds the best simple sequence for getting from oldcol
175  * to newcol to outseq. simp_col() considers (back_)tab and
176  * cursor_left/right.
177  */
178 static void
179 simp_col(struct Sequence *outseq, int oc, int nc)
180 {
181 	t_steps	*dir;
182 	int	dist, tabs, tabstop;
183 	struct Sequence	seqA, seqB, *best, *try;
184 	static t_steps	right = { __MOVE_TAB, __MOVE_RIGHT, __MOVE_LEFT };
185 	static t_steps	left = { __MOVE_BACK_TAB, __MOVE_LEFT, __MOVE_RIGHT };
186 
187 	if (oc == nc)
188 		return;
189 
190 	tabs = tabstop = 0;
191 	best = &seqA;
192 	try = &seqB;
193 
194 	if (oc < nc) {
195 		dir = &right;
196 
197 		if (0 < init_tabs) {
198 			/* Tabstop preceeding nc. */
199 			tabstop = nc / init_tabs;
200 
201 			tabs = tabstop - oc / init_tabs;
202 			if (0 < tabs)
203 				/* Set oc to tabstop before nc : oc <= nc. */
204 				oc = tabstop * init_tabs;
205 
206 			/* Distance from next tabstop to nc in columns. */
207 			tabstop = init_tabs - nc % init_tabs;
208 		}
209 
210 		dist = nc - oc;
211 	} else {
212 		dir = &left;
213 
214 		if (0 < init_tabs) {
215 			/* Tabstop preceeding nc. */
216 			tabstop = nc / init_tabs;
217 
218 			tabs = (oc - 1) / init_tabs - tabstop;
219 			if (0 < tabs)
220 				/* Set oc to tabstop after nc : nc <= oc. */
221 				oc = (tabstop + 1) * init_tabs;
222 
223 			/* Distance from tabstop preceeding nc in columns. */
224 			tabstop = nc % init_tabs;
225 		}
226 
227 		dist = oc - nc;
228 	}
229 
230 	if (0 < tabs) {
231 		/* Tab as close as possible to nc. */
232 		zero_seq(best);
233 		add_op(best, dir->_tab, tabs, 0);
234 		add_seq(outseq, best);
235 
236 		/* If tabs alone get us there, then stop. */
237 		if (oc == nc)
238 			return;
239 	}
240 
241 	/*
242 	 * We're not exactly positioned yet.  Compare the worth of
243 	 * two sequences :
244 	 *   1.	single-step to location;
245 	 *   2.	over tab by one tabstop, then single-step back to location.
246 	 */
247 
248 	/* 1. Single-step to location. */
249 	zero_seq(best);
250 	add_op(best, dir->_one, dist, 0);
251 
252 	/* 2. Over tab by one tabstop, then single-step back to location. */
253 	if (0 < tabstop &&
254 		(nc < columns-init_tabs || auto_left_margin ||
255 		eat_newline_glitch)) {
256 		zero_seq(try);
257 		add_op(try, dir->_tab, 1, 0);
258 
259 		/*
260 		 * vt100 terminals only wrap the cursor when a spacing
261 		 * character is written.  Control characters like <tab>
262 		 * will not cause a line wrap.  Adjust the number of
263 		 * columns to backup by to reflect the cursor having been
264 		 * placed in the last column.  See O'Reilly Termcap &
265 		 * Terminfo book.
266 		 */
267 		if (eat_newline_glitch && columns <= nc + tabstop)
268 			tabstop = columns - nc - 1;
269 
270 		add_op(try, dir->_opp, tabstop, 0);
271 		Make_seq_best(best, try);
272 	}
273 
274 	add_seq(outseq, best);
275 }
276 
277 /*
278  * column() adds the best sequence for moving the cursor from oldcol
279  * to newcol to outseq.
280  *
281  * column() considers column_address, parm_left/right_cursor,
282  * simp_col() and carriage_return + simp_col().
283  */
284 static void
285 column(struct Sequence *outseq, int ocol, int ncol)
286 {
287 	struct Sequence	seqA, seqB;
288 	struct Sequence	*best = &seqA;
289 	struct Sequence	*try = &seqB;
290 	int	parm_cursor, dist;
291 
292 	if (ncol == ocol)
293 		return;
294 
295 	/* try out direct column addressing */
296 	zero_seq(best);
297 	add_op(best, __MOVE_COLUMN, ncol, 0);
298 
299 	/* try out paramaterized left or right motion */
300 	if (ncol < ocol) {
301 		parm_cursor = __MOVE_N_LEFT;
302 		dist = ocol - ncol;
303 	} else {
304 		parm_cursor = __MOVE_N_RIGHT;
305 		dist = ncol - ocol;
306 	}
307 	zero_seq(try);
308 	add_op(try, parm_cursor, dist, 0);
309 	Make_seq_best(best, try);
310 
311 	if (ncol < ocol || !relative) {
312 		/* try carriage_return then simp_col() */
313 		zero_seq(try);
314 		add_op(try, __MOVE_RETURN, 1, 0);
315 		simp_col(try, 0, ncol);
316 		Make_seq_best(best, try);
317 	}
318 
319 	/* try getting there by simpl_col() */
320 	zero_seq(try);
321 	simp_col(try, ocol, ncol);
322 	Make_seq_best(best, try);
323 
324 	add_seq(outseq, best);
325 }
326 
327 /*
328  * send relevant terminal sequences to the screen
329  */
330 static int
331 out_seq(struct Sequence *seq, int (*putout)(int))
332 {
333 	long	p1, p2;
334 	int	*ptr, op;
335 
336 	if (__MOVE_INFINITY <= seq->cost)
337 		return (ERR);
338 
339 	for (ptr = seq->vec; ptr < seq->end; ) {
340 		op = *ptr++;
341 		p1 = *ptr++;
342 		p2 = *ptr++;
343 
344 		if (op < __MOVE_MAX_RELATIVE) {
345 			while (0 < p1--)
346 				(void) TPUTS(cur_term->_move[op]._seq, 1,
347 					putout);
348 		} else {
349 			(void) TPUTS(tparm(cur_term->_move[op]._seq, p1, p2,
350 				0, 0, 0, 0, 0, 0, 0), 1, putout);
351 		}
352 	}
353 
354 	return (OK);
355 }
356 
357 /*
358  * Low-level relative cursor motion.  __m_mvcur() looks for the optimal
359  * way to move the cursor from point A to point B.  If either of the
360  * coordinates for point A are -1 then only absolute addressing is used.
361  * If the coordinates are out-of-bounds then they are MODed into bounds.
362  *
363  * Since __m_mvcur() must perform output to various terminals, an API
364  * similar to tputs() and vidputs() was adopted.
365  */
366 int
367 __m_mvcur(int oldrow, int oldcol, int newrow, int newcol, int (*putout)(int))
368 {
369 	struct Sequence	seqA, seqB;	/* allocate work structures */
370 	struct Sequence	col0seq;	/* sequence to get from col0 to nc */
371 	struct Sequence	*best = &seqA;	/* best sequence so far */
372 	struct Sequence	*try = &seqB;	/* next try */
373 
374 	newrow %= lines;
375 	newcol %= columns;
376 
377 	zero_seq(best);
378 
379 	/* try out direct cursor addressing */
380 	add_op(best, __MOVE_ROW_COLUMN, newrow, newcol);
381 
382 	if (newrow == lines-1 && newcol == columns-1) {
383 		/* Force absolute position at bottom right because we	*/
384 		/* don't know where the terminal thinks it is...	*/
385 		return (out_seq(best, putout));
386 	}
387 	if ((relative = (0 <= oldrow && 0 <= oldcol)) != 0) {
388 		oldrow %= lines;
389 		oldcol %= columns;
390 
391 		/* try out independent row/column addressing */
392 		zero_seq(try);
393 		row(try, oldrow, newrow);
394 		column(try, oldcol, newcol);
395 		Make_seq_best(best, try);
396 	}
397 	if (newcol < oldcol || !relative) {
398 		zero_seq(&col0seq);
399 		column(&col0seq, 0, newcol);
400 		if (col0seq.cost < __MOVE_INFINITY) {
401 			/* try out homing and then row/column */
402 			if (newrow < oldrow || !relative) {
403 				zero_seq(try);
404 				add_op(try, __MOVE_HOME, 1, 0);
405 				row(try, 0, newrow);
406 				add_seq(try, &col0seq);
407 				Make_seq_best(best, try);
408 			}
409 
410 			/* try out homing to last line  and then row/column */
411 			if (newrow > oldrow || !relative) {
412 				zero_seq(try);
413 				add_op(try, __MOVE_LAST_LINE, 1, 0);
414 				row(try, lines - 1, newrow);
415 				add_seq(try, &col0seq);
416 				Make_seq_best(best, try);
417 			}
418 		}
419 	}
420 
421 	return (out_seq(best, putout));
422 }
423 
424 /*
425  * A do nothing output function for tputs().
426  */
427 static int
428 nilout(int ch)
429 {
430 	return (ch);
431 }
432 
433 /*
434  * Initialize an entry in cur_term->_move[] with parameters p1 and p2.
435  * Note that some capabilities will ignore their parameters.
436  */
437 static void
438 cost(char *cap, int index, int p1, int p2)
439 {
440 	cur_term->_move[index]._seq = cap;
441 
442 	if (cap == (char *) 0 || cap[0] == '\0') {
443 		cur_term->_move[index]._cost = __MOVE_INFINITY;
444 	} else {
445 		cur_term->_move[index]._cost = __m_tputs(
446 			tparm(cap, (long) p1, (long) p2, 0, 0, 0, 0, 0, 0, 0),
447 			1, nilout);
448 
449 		if (cap == cursor_down && strchr(cap, '\n') != (char *) 0)
450 			cur_term->_move[index]._cost = __MOVE_INFINITY;
451 	}
452 }
453 
454 void
455 __m_mvcur_cost(void)
456 {
457 	/*
458 	 * Relative cursor motion that will be costed on a per
459 	 * character basis in __m_mvcur().
460 	 */
461 	cost(cursor_up, __MOVE_UP, 0, 0);
462 	cost(cursor_down, __MOVE_DOWN, 0, 0);
463 	cost(cursor_left, __MOVE_LEFT, 0, 0);
464 	cost(cursor_right, __MOVE_RIGHT, 0, 0);
465 	cost(dest_tabs_magic_smso ? NULL : tab, __MOVE_TAB, 0, 0);
466 	cost(dest_tabs_magic_smso ? NULL : back_tab,
467 		__MOVE_BACK_TAB, 0, 0);
468 
469 	/* Absolute cursor motion with fixed cost. */
470 	cost(cursor_home, __MOVE_HOME, 0, 0);
471 	cost(cursor_to_ll, __MOVE_LAST_LINE, 0, 0);
472 	cost(carriage_return, __MOVE_RETURN, 0, 0);
473 
474 	/* Parameter cursor motion with worst case cost. */
475 	cost(row_address, __MOVE_ROW, lines-1, 0);
476 	cost(parm_up_cursor, __MOVE_N_UP, lines-1, 0);
477 	cost(parm_down_cursor, __MOVE_N_DOWN, lines-1, 0);
478 	cost(column_address, __MOVE_COLUMN, columns-1, 0);
479 	cost(parm_left_cursor, __MOVE_N_LEFT, columns-1, 0);
480 	cost(parm_right_cursor, __MOVE_N_RIGHT, columns-1, 0);
481 	cost(cursor_address, __MOVE_ROW_COLUMN, lines-1, columns-1);
482 }
483 
484 #undef mvcur
485 
486 int
487 mvcur(int oy, int ox, int ny, int nx)
488 {
489 	return (__m_mvcur(oy, ox, ny, nx, __m_outc));
490 }
491