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