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, by Sun Microsystems, Inc.
24 * All rights reserved.
25 */
26
27 /*
28 * mvcur.c
29 *
30 * XCurses Library
31 *
32 * Copyright 1990, 1995 by Mortice Kern Systems Inc. All rights reserved.
33 *
34 */
35
36 #ifdef M_RCSID
37 #ifndef lint
38 static char rcsID[] = "$Header: /rd/src/libc/xcurses/rcs/mvcur.c 1.4 1995/06/15 18:56:03 ant Exp $";
39 #endif
40 #endif
41
42 #include <private.h>
43 #include <string.h>
44 #include <stdarg.h>
45
46 #define VECTOR_SIZE 128 /* size of strategy buffer */
47
48 /*
49 * #define
50 * Make_seq_best(s1, s2)
51 *
52 * Make_seq_best() swaps the values of the pointers if s1->cost > s2->cost.
53 */
54 #define Make_seq_best(s1, s2) \
55 if (s1->cost > s2->cost) { \
56 struct Sequence* temp = s1; \
57 s1 = s2; \
58 s2 = temp; \
59 }
60
61 #define zero_seq(seq) ((seq)->end = (seq)->vec, (seq)->cost = 0)
62
63 struct Sequence {
64 int vec[VECTOR_SIZE]; /* vector of operations */
65 int *end; /* end of vector */
66 int cost; /* cost of vector */
67 };
68
69 static bool relative; /* set if we really know where we are */
70
71 /*f
72 * Add sequence 2 to sequence 1.
73 */
74 STATIC void
add_seq(seq1,seq2)75 add_seq(seq1, seq2)
76 struct Sequence *seq1, *seq2;
77 {
78 if (seq1->cost >= __MOVE_INFINITY || seq2->cost >= __MOVE_INFINITY)
79 seq1->cost = __MOVE_INFINITY;
80 else {
81 int* vptr = seq2->vec;
82 while (vptr != seq2->end)
83 *(seq1->end++) = *(vptr++);
84 seq1->cost += seq2->cost;
85 }
86 }
87
88 /*f
89 * add_op() adds the operator op and the appropriate
90 * number of paramaters to seq. It also increases the
91 * cost appropriately.
92 *
93 * If op takes no parameters then p0 is taken to be a count.
94 */
95 STATIC void
add_op(seq,op,p1,p2)96 add_op(seq, op, p1, p2)
97 struct Sequence *seq;
98 int op, p1, p2;
99 {
100 *(seq->end++) = op;
101 *(seq->end++) = p1;
102 *(seq->end++) = p2;
103
104 if (cur_term->_move[op]._seq == (char *) 0) {
105 seq->cost = __MOVE_INFINITY;
106 } else if (op < __MOVE_MAX_RELATIVE) {
107 /* No parameters, total is cost * p1. */
108 seq->cost += cur_term->_move[op]._cost * p1;
109 } else {
110 /* Cursor motion using parameters have fixed cost. */
111 seq->cost = cur_term->_move[op]._cost;
112 }
113 }
114
115 /*f
116 * row() adds the best sequence for moving the cursor from orow
117 * to nrow to seq.
118 *
119 * row() considers row_address, parm_up/down_cursor and cursor_up/down.
120 */
121 STATIC void
row(outseq,orow,nrow)122 row(outseq, orow, nrow)
123 struct Sequence *outseq;
124 int orow, nrow;
125 {
126 struct Sequence seqA, seqB;
127 struct Sequence* best = &seqA;
128 struct Sequence* try = &seqB;
129 int parm_cursor, one_step, dist;
130
131 if (nrow == orow)
132 return;
133
134 if (nrow < orow) {
135 parm_cursor = __MOVE_N_UP;
136 one_step = __MOVE_UP;
137 dist = orow - nrow;
138 } else {
139 parm_cursor = __MOVE_N_DOWN;
140 one_step = __MOVE_DOWN;
141 dist = nrow - orow;
142 }
143
144 /* try out direct row addressing */
145 zero_seq(best);
146 add_op(best, __MOVE_ROW, nrow, 0);
147
148 /* try out paramaterized up or down motion */
149 zero_seq(try);
150 add_op(try, parm_cursor, dist, 0);
151 Make_seq_best(best, try);
152
153 /* try getting there one step at a time... */
154 zero_seq(try);
155 add_op(try, one_step, dist, 0);
156 Make_seq_best(best, try);
157
158 add_seq(outseq, best);
159 }
160
161 /*
162 * Motion indexes used in simp_col().
163 */
164 typedef struct {
165 int _tab; /* Tab index. */
166 int _one; /* Single-step index, same direction as tab. */
167 int _opp; /* Single-step index, opposite direction to tab. */
168 } t_steps;
169
170 /*f
171 * simp_col(outseq, oldcol, newcol)
172 *
173 * simp_col() adds the best simple sequence for getting from oldcol
174 * to newcol to outseq. simp_col() considers (back_)tab and
175 * cursor_left/right.
176 */
177 STATIC void
simp_col(outseq,oc,nc)178 simp_col(outseq, oc, nc)
179 struct Sequence *outseq;
180 int oc, nc;
181 {
182 t_steps *dir;
183 int dist, tabs, tabstop;
184 struct Sequence seqA, seqB, *best, *try;
185 static t_steps right = { __MOVE_TAB, __MOVE_RIGHT, __MOVE_LEFT };
186 static t_steps left = { __MOVE_BACK_TAB, __MOVE_LEFT, __MOVE_RIGHT };
187
188 if (oc == nc)
189 return;
190
191 tabs = tabstop = dist = 0;
192 best = &seqA;
193 try = &seqB;
194
195 if (oc < nc) {
196 dir = &right;
197
198 if (0 < init_tabs) {
199 /* Tabstop preceeding nc. */
200 tabstop = nc / init_tabs;
201
202 tabs = tabstop - oc / init_tabs;
203 if (0 < tabs)
204 /* Set oc to tabstop before nc : oc <= nc. */
205 oc = tabstop * init_tabs;
206
207 /* Distance from next tabstop to nc in columns. */
208 tabstop = init_tabs - nc % init_tabs;
209 }
210
211 dist = nc - oc;
212 } else {
213 dir = &left;
214
215 if (0 < init_tabs) {
216 /* Tabstop preceeding nc. */
217 tabstop = nc / init_tabs;
218
219 tabs = (oc - 1) / init_tabs - tabstop;
220 if (0 < tabs)
221 /* Set oc to tabstop after nc : nc <= oc. */
222 oc = (tabstop + 1) * init_tabs;
223
224 /* Distance from tabstop preceeding nc in columns. */
225 tabstop = nc % init_tabs;
226 }
227
228 dist = oc - nc;
229 }
230
231 if (0 < tabs) {
232 /* Tab as close as possible to nc. */
233 zero_seq(best);
234 add_op(best, dir->_tab, tabs, 0);
235 add_seq(outseq, best);
236
237 /* If tabs alone get us there, then stop. */
238 if (oc == nc)
239 return;
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 || eat_newline_glitch)) {
255 zero_seq(try);
256 add_op(try, dir->_tab, 1, 0);
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 /*f
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(outseq,ocol,ncol)283 column(outseq, ocol, ncol)
284 struct Sequence* outseq;
285 int ocol, 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 /*f
328 * send relevant terminal sequences to the screen
329 */
330 STATIC int
out_seq(seq,putout)331 out_seq(seq, putout)
332 struct Sequence *seq;
333 int (*putout) ANSI((int));
334 {
335 long p1, p2;
336 int *ptr, op;
337
338 if (__MOVE_INFINITY <= seq->cost)
339 return ERR;
340
341 for (ptr = seq->vec; ptr < seq->end; ) {
342 op = *ptr++;
343 p1 = *ptr++;
344 p2 = *ptr++;
345
346 if (op < __MOVE_MAX_RELATIVE) {
347 while (0 < p1--)
348 (void) tputs(
349 cur_term->_move[op]._seq, 1, putout
350 );
351 } else {
352 (void) tputs(
353 tparm(
354 cur_term->_move[op]._seq, p1, p2,
355 0, 0, 0, 0, 0, 0, 0
356 ), 1, putout
357 );
358 }
359 }
360
361 return OK;
362 }
363
364 /*f
365 * Low-level relative cursor motion. __m_mvcur() looks for the optimal
366 * way to move the cursor from point A to point B. If either of the
367 * coordinates for point A are -1 then only absolute addressing is used.
368 * If the coordinates are out-of-bounds then they are MODed into bounds.
369 *
370 * Since __m_mvcur() must perform output to various terminals, an API
371 * similar to tputs() and vidputs() was adopted.
372 */
373 int
374 __m_mvcur(oldrow, oldcol, newrow, newcol, putout)
375 int oldrow, oldcol, newrow, newcol, (*putout)(int);
376 {
377 struct Sequence seqA, seqB; /* allocate work structures */
378 struct Sequence col0seq; /* sequence to get from col0 to nc */
379 struct Sequence* best = &seqA; /* best sequence so far */
380 struct Sequence* try = &seqB; /* next try */
381
382 #ifdef M_CURSES_TRACE
383 __m_trace(
384 "__m_mvcur(%d, %d, %d, %d, %p)",
385 oldrow, oldcol, newrow, newcol, putout
386 );
387 #endif
388
389 newrow %= lines;
390 newcol %= columns;
391
392 zero_seq(best);
393
394 /* try out direct cursor addressing */
395 add_op(best, __MOVE_ROW_COLUMN, newrow, newcol);
396
397 if((relative = 0 <= oldrow && 0 <= oldcol)){
398 oldrow %= lines;
399 oldcol %= columns;
400
401 /* try out independent row/column addressing */
402 zero_seq(try);
403 row(try, oldrow, newrow);
404 column(try, oldcol, newcol);
405 Make_seq_best(best, try);
406 }
407 if (newcol < oldcol || !relative){
408 zero_seq(&col0seq);
409 column(&col0seq, 0, newcol);
410 if (col0seq.cost < __MOVE_INFINITY) {
411 /* try out homing and then row/column */
412 if (newrow < oldrow || !relative) {
413 zero_seq(try);
414 add_op(try, __MOVE_HOME, 1, 0);
415 row(try, 0, newrow);
416 add_seq(try, &col0seq);
417 Make_seq_best(best, try);
418 }
419
420 /* try out homing to last line and then row/column */
421 if (newrow > oldrow || !relative) {
422 zero_seq(try);
423 add_op(try, __MOVE_LAST_LINE, 1, 0);
424 row(try, lines - 1, newrow);
425 add_seq(try, &col0seq);
426 Make_seq_best(best, try);
427 }
428 }
429 }
430
431 return __m_return_code("__m_mvcur", out_seq(best, putout));
432 }
433
434 /*
435 * A do nothing output function for tputs().
436 */
437 STATIC int
nilout(ch)438 nilout(ch)
439 int ch;
440 {
441 return ch;
442 }
443
444 /*
445 * Initialize an entry in cur_term->_move[] with parameters p1 and p2.
446 * Note that some capabilities will ignore their parameters.
447 */
448 STATIC void
cost(cap,index,p1,p2)449 cost(cap, index, p1, p2)
450 char *cap;
451 int index, p1, p2;
452 {
453 cur_term->_move[index]._seq = cap;
454
455 if (cap == (char *) 0 || cap[0] == '\0') {
456 cur_term->_move[index]._cost = __MOVE_INFINITY;
457 } else {
458 cur_term->_move[index]._cost = tputs(
459 tparm(cap, (long) p1, (long) p2, 0, 0, 0, 0, 0, 0, 0),
460 1, nilout
461 );
462
463 if (cap == cursor_down && strchr(cap, '\n') != (char *) 0)
464 cur_term->_move[index]._cost = __MOVE_INFINITY;
465 }
466 }
467
468 void
__m_mvcur_cost()469 __m_mvcur_cost()
470 {
471 /* Relative cursor motion that will be costed on a per
472 * character basis in __m_mvcur().
473 */
474 cost(cursor_up, __MOVE_UP, 0, 0);
475 cost(cursor_down, __MOVE_DOWN, 0, 0);
476 cost(cursor_left, __MOVE_LEFT, 0, 0);
477 cost(cursor_right, __MOVE_RIGHT, 0, 0);
478 cost(dest_tabs_magic_smso ? (char *) 0 : tab, __MOVE_TAB, 0, 0);
479 cost(
480 dest_tabs_magic_smso ? (char *) 0
481 : back_tab, __MOVE_BACK_TAB, 0, 0
482 );
483
484 /* Absolute cursor motion with fixed cost. */
485 cost(cursor_home, __MOVE_HOME, 0, 0);
486 cost(cursor_to_ll, __MOVE_LAST_LINE, 0, 0);
487 cost(carriage_return, __MOVE_RETURN, 0, 0);
488
489 /* Parameter cursor motion with worst case cost. */
490 cost(row_address, __MOVE_ROW, lines-1, 0);
491 cost(parm_up_cursor, __MOVE_N_UP, lines-1, 0);
492 cost(parm_down_cursor, __MOVE_N_DOWN, lines-1, 0);
493 cost(column_address, __MOVE_COLUMN, columns-1, 0);
494 cost(parm_left_cursor, __MOVE_N_LEFT, columns-1, 0);
495 cost(parm_right_cursor, __MOVE_N_RIGHT, columns-1, 0);
496 cost(cursor_address, __MOVE_ROW_COLUMN, lines-1, columns-1);
497 }
498
499 int
500 (mvcur)(oy, ox, ny, nx)
501 int oy, ox, ny, nx;
502 {
503 #ifdef M_CURSES_TRACE
504 __m_trace("mvcur(%d, %d, %d, %d)", oy, ox, ny, nx);
505 #endif
506
507 return __m_return_code("mvcur", __m_mvcur(oy, ox, ny, nx, __m_outc));
508 }
509
510