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