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
add_seq(struct Sequence * seq1,struct Sequence * seq2)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
add_op(struct Sequence * seq,int op,int p1,int p2)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
row(struct Sequence * outseq,int orow,int nrow)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
simp_col(struct Sequence * outseq,int oc,int nc)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
column(struct Sequence * outseq,int ocol,int ncol)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
out_seq(struct Sequence * seq,int (* putout)(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
__m_mvcur(int oldrow,int oldcol,int newrow,int newcol,int (* putout)(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
nilout(int ch)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
cost(char * cap,int index,int p1,int p2)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
__m_mvcur_cost(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
mvcur(int oy,int ox,int ny,int nx)487 mvcur(int oy, int ox, int ny, int nx)
488 {
489 return (__m_mvcur(oy, ox, ny, nx, __m_outc));
490 }
491