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