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 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 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 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 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 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 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 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 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 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