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 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 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 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 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 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 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 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 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 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 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 487 mvcur(int oy, int ox, int ny, int nx) 488 { 489 return (__m_mvcur(oy, ox, ny, nx, __m_outc)); 490 } 491