xref: /illumos-gate/usr/src/lib/libcurses/screen/mvcur.c (revision eb9a1df2aeb866bf1de4494433b6d7e5fa07b3ae)
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 1997 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1988 AT&T	*/
28 /*	  All Rights Reserved	*/
29 
30 /*
31  * University Copyright- Copyright (c) 1982, 1986, 1988
32  * The Regents of the University of California
33  * All Rights Reserved
34  *
35  * University Acknowledgment- Portions of this document are derived from
36  * software developed by the University of California, Berkeley, and its
37  * contributors.
38  */
39 
40 #pragma ident	"%Z%%M%	%I%	%E% SMI"
41 
42 /*LINTLIBRARY*/
43 
44 #include	<sys/types.h>
45 #include	"curses_inc.h"
46 
47 /*
48  * Cursor motion optimization routine.  This routine takes as parameters
49  * the screen positions that the cursor is currently at, and the position
50  * you want it to be at, and it will move the cursor there very
51  * efficiently.  It isn't really optimal, since several approximations
52  * are taken in the interests of efficiency and simplicity.  The code
53  * here considers directly addressing the cursor, and also considers
54  * local motions using left, right, up, down, tabs, backtabs, vertical
55  * and horizontal addressing, and parameterized motions.  It does not
56  * consider using home down, or taking advantage of automatic margins on
57  * any of the four directions.  (Two of these directions, left and right,
58  * are well defined by the am and bw capabilities, but up and down are
59  * not defined, nor are tab or backtab off the ends.)
60  *
61  * General strategies considered:
62  *	CA	Direct Cursor Addressing
63  *	LM	Local Motions from the old position
64  *	HR	Home + Local Motions from upper left corner
65  *	HDR	Home Down + Local Motions from lower left corner
66  *	CR	CR + Local Motions from left margin
67  *
68  * Local Motions can include
69  *	Up	cuu, cuu1, vpa
70  *	Down	cud, cud1, vpa
71  *	Left	cul, cul1, hpa, bs, cbt
72  *	Right	cuf, cuf1, hpa, tab, char moved over
73  */
74 
75 /* This is called _ISMARK2 so it doesn't conflict with _ISMARK1 in wrefresh.c */
76 
77 #define	_ISMARK2(x)	(mks[(x) / BITSPERBYTE] & (1<<((x) % BITSPERBYTE)))
78 
79 #define	H_UP	-1
80 #define	H_DO	1
81 
82 static	int	Newy;
83 static  int	_homefirst(int, int, int, int),
84 		_mvrel(int, int, int, int, int),
85 		_mvvert(int, int, int), _mvhor(int, int, int),
86 		_mvright(int, int, int), _mvleft(int, int, int);
87 
88 int
89 mvcur(int cury, int curx, int newy, int newx)
90 {
91 	int	hu,	/* cost home + relative */
92 		hd,	/* cost home-down + relative */
93 		rl,	/* cost relative */
94 		cm;	/* cost direct cursor motion */
95 
96 	/* obvious case */
97 	if (cury == newy && curx == newx)
98 		return (OK);
99 
100 	/* not in the right mode for cursor movement */
101 	if (SP->fl_endwin)
102 		return (ERR);
103 
104 	if (!move_standout_mode && curscr->_attrs && !SP->_mks)
105 		_VIDS(A_NORMAL, curscr->_attrs);
106 
107 	if (!move_insert_mode && SP->phys_irm)
108 		_OFFINSERT();
109 
110 	Newy = newy;
111 
112 	/* cost of using cm */
113 	cm = _COST(Cursor_address);
114 
115 	rl = hd = hu = LARGECOST;
116 
117 	/* baudrate optimization */
118 	if (cm < LARGECOST && SP->baud >= 2400 &&
119 	    cury >= 0 && cury < curscr->_maxy &&
120 	    curx >= 0 && curx < curscr->_maxx) {
121 		if (cursor_down && (newy == (cury + 1)) &&
122 		    ((newx == curx) || (newx == 0 && carriage_return))) {
123 			if (newx != curx)
124 				_PUTS(carriage_return, 1);
125 			_PUTS(cursor_down, 1);
126 			goto done;
127 		}
128 
129 		/* fast horizontal move */
130 		if (cury == newy && newx < curx - 4 && newx > curx + 4) {
131 			if (newx < curx)
132 				rl = _mvleft(curx, newx, FALSE);
133 			else
134 				rl = _mvright(curx, newx, FALSE);
135 			if (rl < cm) {
136 				if (newx < curx)
137 					rl = _mvleft(curx, newx, TRUE);
138 				else
139 					rl = _mvright(curx, newx, TRUE);
140 				goto done;
141 			}
142 		}
143 	}
144 
145 	/* cost using relative movements */
146 	if (rl >= LARGECOST && cury >= 0 && cury < curscr->_maxy &&
147 	    curx >= 0 && curx < curscr->_maxx)
148 		rl = _mvrel(cury, curx, newy, newx, FALSE);
149 
150 	/* cost of homing to upper-left corner first */
151 	if (cursor_home)
152 		hu = _homefirst(newy, newx, H_UP, FALSE);
153 
154 	/* cost of homing to lower-left corner first */
155 	if (cursor_to_ll)
156 		hd = _homefirst(newy, newx, H_DO, FALSE);
157 
158 	/* can't do any one of them */
159 	if (cm >= LARGECOST && rl >= LARGECOST && hu >= LARGECOST &&
160 	    hd >= LARGECOST)
161 		return (ERR);
162 
163 	/* do the best one */
164 	if (cm <= rl && cm <= hu && cm <= hd)
165 		_PUTS(tparm_p2(cursor_address, newy, newx), 1);
166 	else
167 		if (rl <= hu && rl <= hd)
168 			(void) _mvrel(cury, curx, newy, newx, TRUE);
169 		else
170 			(void) _homefirst(newy, newx, hu <= hd ?
171 			    H_UP : H_DO, TRUE);
172 
173 done:
174 	/* update cursor position */
175 	/*LINTED*/
176 	curscr->_curx = (short) newx;
177 	/*LINTED*/
178 	curscr->_cury = (short) newy;
179 
180 	return (OK);
181 }
182 
183 /* Move by homing first. */
184 
185 static int
186 _homefirst(int ny, int nx, int type, int doit)
187 {
188 	char	*home;
189 	int	cy, cost;
190 
191 	if (type == H_UP) {
192 		home = cursor_home;
193 		cost = _COST(Cursor_home);
194 		cy = 0;
195 	} else {
196 		home = cursor_to_ll;
197 		cost = _COST(Cursor_to_ll);
198 		cy = curscr->_maxy - 1;
199 	}
200 
201 	if (!home)
202 		return (LARGECOST);
203 	if (!doit)
204 		return (cost + _mvrel(cy, 0, ny, nx, FALSE));
205 
206 	_PUTS(home, 1);
207 	return (_mvrel(cy, 0, ny, nx, TRUE));
208 }
209 
210 /* Move relatively */
211 
212 static int
213 _mvrel(int cy, int cx, int ny, int nx, int doit)
214 {
215 	int	cv, ch;
216 
217 	/* do in this order since _mvhor may need the curscr image */
218 	cv = _mvvert(cy, ny, doit);
219 	ch = _mvhor(cx, nx, doit);
220 
221 	return (cv + ch);
222 }
223 
224 /* Move vertically */
225 
226 static int
227 _mvvert(int cy, int ny, int doit)
228 {
229 	char	*ve;
230 	int	dy, st_1, st_n, cv;
231 
232 	if (cy == ny)
233 		goto out;
234 
235 	/* cost of stepwise movement */
236 	if (cy < ny) {
237 		dy = ny-cy;
238 		st_1 = _COST(Cursor_down) * dy;
239 		st_n = _COST(Parm_down_cursor);
240 	} else {
241 		dy = cy-ny;
242 		st_1 = _COST(Cursor_up) * dy;
243 		st_n = _COST(Parm_up_cursor);
244 	}
245 
246 	/* cost of using vertical move */
247 	cv = _COST(Row_address);
248 
249 	/* if calculating cost only */
250 	if (!doit)
251 		return ((cv < st_1 && cv < st_n) ? cv :
252 		    (st_n < st_1) ? st_n : st_1);
253 
254 	/* do it */
255 	if (cv < st_1 && cv < st_n)
256 		_PUTS(tparm_p1(row_address, ny), 1);
257 	else
258 		if (st_n < st_1) {
259 			if (cy < ny)
260 				_PUTS(tparm_p1(parm_down_cursor, dy), 1);
261 			else
262 				_PUTS(tparm_p1(parm_up_cursor, dy), 1);
263 		} else {
264 			if (cy < ny)
265 				ve = cursor_down;
266 			else
267 				ve = cursor_up;
268 			for (; dy > 0; --dy)
269 				_PUTS(ve, 1);
270 		}
271 
272 out:
273 	return (0);
274 }
275 
276 /* Move horizontally */
277 
278 static int
279 _mvhor(int cx, int nx, int doit)
280 {
281 	int	st, ch, hl;
282 
283 	if (cx == nx)
284 		goto out;
285 
286 	/* cost using horizontal move */
287 	ch = _COST(Row_address);
288 
289 	/* cost doing stepwise */
290 	st = cx < nx ? _mvright(cx, nx, FALSE) : _mvleft(cx, nx, FALSE);
291 
292 	/* cost homeleft first */
293 	hl = (_COST(Carriage_return) < LARGECOST) ?
294 	    _COST(Carriage_return) + _mvright(0, nx, FALSE) : LARGECOST;
295 
296 	if (!doit)
297 		return ((ch < st && ch < hl) ? ch : (hl < st ? hl : st));
298 
299 	if (ch < st && ch < hl)
300 		_PUTS(tparm_p1(column_address, nx), 1);
301 	else
302 		if (hl < st) {
303 			_PUTS(carriage_return, 1);
304 			(void) _mvright(0, nx, TRUE);
305 		} else {
306 			if (cx < nx)
307 				(void) _mvright(cx, nx, TRUE);
308 			else
309 				(void) _mvleft(cx, nx, TRUE);
310 	}
311 out:
312 	return (0);
313 }
314 
315 /* Move right. */
316 
317 static int
318 _mvright(int cx, int nx, int doit)
319 {
320 	chtype	*scp;
321 	char	*mks;
322 	int	nt, tx, x, stcost, iscont;
323 
324 	if (!cursor_right && !parm_right_cursor)
325 		return (LARGECOST);
326 
327 	scp = curscr->_y[Newy];
328 	mks = magic_cookie_glitch >= 0 ? SP->_mks[Newy] : NULL;
329 
330 	if (cursor_right) {
331 		/* number of tabs used in stepwise movement */
332 		nt = tab ? (nx / TABSIZE - cx / TABSIZE) : 0;
333 		tx = (nt > 0) ? (cx / TABSIZE + nt) * TABSIZE : cx;
334 
335 		/* calculate stepwise cost */
336 		stcost = nt * _COST(Tab);
337 		iscont = 0;
338 		for (x = tx; x < nx; ++x) {
339 			if (iscont == 0 && !ISCBIT(scp[x]))
340 				iscont = 1;
341 			if ((!ceol_standout_glitch && !mks &&
342 			    _ATTR(scp[x]) == curscr->_attrs) ||
343 			    ceol_standout_glitch || (mks && !_ISMARK2(x))) {
344 				if (!ISMBIT(scp[x]))
345 					stcost += 1;
346 				else if (iscont && !(nx - x == 1 && nx <
347 				    curscr->_maxx && ISCBIT(scp[nx])))
348 					stcost += 1;
349 				else
350 					stcost += _COST(Cursor_right);
351 			} else
352 				stcost += _COST(Cursor_right);
353 		}
354 	} else
355 		stcost = LARGECOST;
356 
357 	if (!doit)
358 		return ((_COST(Parm_right_cursor) < stcost) ?
359 		    _COST(Parm_right_cursor) : stcost);
360 
361 	/* actually move */
362 	if (_COST(Parm_right_cursor) < stcost)
363 		_PUTS(tparm_p1(parm_right_cursor, nx-cx), 1);
364 	else {
365 		if (SP->phys_irm)
366 			_OFFINSERT();
367 		for (; nt > 0; --nt)
368 			_PUTS(tab, 1);
369 		iscont = 0;
370 		for (x = tx; x < nx; ++x) {
371 			if (iscont == 0 && !ISCBIT(scp[x]))
372 				iscont = 1;
373 			if ((!ceol_standout_glitch && !mks &&
374 			    _ATTR(scp[x]) == curscr->_attrs) ||
375 			    ceol_standout_glitch || (mks && !_ISMARK2(x))) {
376 				if (!ISMBIT(scp[x]))
377 					(void) _outwch(_CHAR(scp[x]));
378 				else if (iscont && !(nx - x == 1 &&
379 				    nx < curscr->_maxx && ISCBIT(scp[nx])))
380 					(void) _outwch(_CHAR(scp[x]));
381 				else
382 					_PUTS(cursor_right, 1);
383 			} else
384 				_PUTS(cursor_right, 1);
385 		}
386 	}
387 
388 	return (0);
389 }
390 
391 /* Move left */
392 
393 static int
394 _mvleft(int cx, int nx, int doit)
395 {
396 	int	tx, nt, x, stcost;
397 
398 	if (!cursor_left && !parm_left_cursor)
399 		return (LARGECOST);
400 
401 	if (cursor_left) {
402 		/* stepwise cost */
403 		tx = cx;
404 		nt = 0;
405 		if (back_tab) {
406 			/* the TAB position >= nx */
407 			x = (nx % TABSIZE) ? (nx / TABSIZE + 1) * TABSIZE : nx;
408 
409 			/* # of tabs used and position after using them */
410 			if (x < cx) {
411 				nt = (cx / TABSIZE - x / TABSIZE) +
412 				    ((cx % TABSIZE) ? 1 : 0);
413 				tx = x;
414 			}
415 		}
416 		stcost = nt * _COST(Back_tab) + (tx-nx) * _COST(Cursor_left);
417 	} else
418 		stcost = LARGECOST;
419 
420 	/* get cost only */
421 	if (!doit)
422 		return ((_COST(Parm_left_cursor) < stcost) ?
423 		    _COST(Parm_left_cursor) : stcost);
424 
425 	/* doit */
426 	if (_COST(Parm_left_cursor) < stcost)
427 		_PUTS(tparm_p1(parm_left_cursor, cx - nx), 1);
428 	else {
429 		for (; nt > 0; --nt)
430 		    _PUTS(back_tab, 1);
431 		for (; tx > nx; --tx)
432 		    _PUTS(cursor_left, 1);
433 	}
434 
435 	return (0);
436 }
437