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