1 /* 2 * Copyright (c) 1980, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char sccsid[] = "@(#)move.c 8.1 (Berkeley) 07/19/93"; 10 #endif /* not lint */ 11 12 /************************************************************************* 13 * 14 * MOVE LIBRARY 15 * 16 * This set of subroutines moves a cursor to a predefined 17 * location, independent of the terminal type. If the 18 * terminal has an addressable cursor, it uses it. If 19 * not, it optimizes for tabs (currently) even if you don't 20 * have them. 21 * 22 * At all times the current address of the cursor must be maintained, 23 * and that is available as structure cursor. 24 * 25 * The following calls are allowed: 26 * move(sp) move to point sp. 27 * up() move up one line. 28 * down() move down one line. 29 * bs() move left one space (except column 0). 30 * nd() move right one space(no write). 31 * clear() clear screen. 32 * home() home. 33 * ll() move to lower left corner of screen. 34 * cr() carriage return (no line feed). 35 * pr() just like standard printf, but keeps track 36 * of cursor position. (Uses pstring). 37 * apr() same as printf, but first argument is &point. 38 * (Uses pstring). 39 * pstring(s) output the string of printing characters. 40 * However, '\r' is interpreted to mean return 41 * to column of origination AND do linefeed. 42 * '\n' causes <cr><lf>. 43 * putpad(str) calls tputs to output character with proper 44 * padding. 45 * outch() the output routine for a character used by 46 * tputs. It just calls putchar. 47 * pch(ch) output character to screen and update 48 * cursor address (must be a standard 49 * printing character). WILL SCROLL. 50 * pchar(ps,ch) prints one character if it is on the 51 * screen at the specified location; 52 * otherwise, dumps it.(no wrap-around). 53 * 54 * getcap() initializes strings for later calls. 55 * cap(string) outputs the string designated in the termcap 56 * data base. (Should not move the cursor.) 57 * done() returns the terminal to intial state and exits. 58 * 59 * point(&p,x,y) return point set to x,y. 60 * 61 * baudrate(x) returns the baudrate of the terminal. 62 * delay(t) causes an approximately constant delay 63 * independent of baudrate. 64 * Duration is ~ t/20 seconds. 65 * 66 ******************************************************************************/ 67 68 #if __STDC__ 69 #include <stdarg.h> 70 #else 71 #include <varargs.h> 72 #endif 73 #include "snake.h" 74 75 int CMlength; 76 int NDlength; 77 int BSlength; 78 int delaystr[10]; 79 short ospeed; 80 81 static char str[80]; 82 83 move(sp) 84 struct point *sp; 85 { 86 int distance; 87 int tabcol,ct; 88 struct point z; 89 90 if (sp->line <0 || sp->col <0 || sp->col > COLUMNS){ 91 pr("move to [%d,%d]?",sp->line,sp->col); 92 return; 93 } 94 if (sp->line >= LINES){ 95 move(point(&z,sp->col,LINES-1)); 96 while(sp->line-- >= LINES)putchar('\n'); 97 return; 98 } 99 100 if (CM != 0) { 101 char *cmstr = tgoto(CM, sp->col, sp->line); 102 103 CMlength = strlen(cmstr); 104 if(cursor.line == sp->line){ 105 distance = sp->col - cursor.col; 106 if(distance == 0)return; /* Already there! */ 107 if(distance > 0){ /* Moving to the right */ 108 if(distance*NDlength < CMlength){ 109 right(sp); 110 return; 111 } 112 if(TA){ 113 ct=sp->col&7; 114 tabcol=(cursor.col|7)+1; 115 do{ 116 ct++; 117 tabcol=(tabcol|7)+1; 118 } 119 while(tabcol<sp->col); 120 if(ct<CMlength){ 121 right(sp); 122 return; 123 } 124 } 125 } else { /* Moving to the left */ 126 if (-distance*BSlength < CMlength){ 127 gto(sp); 128 return; 129 } 130 } 131 if(sp->col < CMlength){ 132 cr(); 133 right(sp); 134 return; 135 } 136 /* No more optimizations on same row. */ 137 } 138 distance = sp->col - cursor.col; 139 distance = distance > 0 ? 140 distance*NDlength : -distance * BSlength; 141 if (distance < 0) 142 pr("ERROR: distance is negative: %d",distance); 143 distance += abs(sp->line - cursor.line); 144 if(distance >= CMlength){ 145 putpad(cmstr); 146 cursor.line = sp->line; 147 cursor.col = sp->col; 148 return; 149 } 150 } 151 152 /* 153 * If we get here we have a terminal that can't cursor 154 * address but has local motions or one which can cursor 155 * address but can get there quicker with local motions. 156 */ 157 gto(sp); 158 } 159 gto(sp) 160 struct point *sp; 161 { 162 163 int distance,f,tfield,j; 164 165 if (cursor.line > LINES || cursor.line <0 || 166 cursor.col <0 || cursor.col > COLUMNS) 167 pr("ERROR: cursor is at %d,%d\n", 168 cursor.line,cursor.col); 169 if (sp->line > LINES || sp->line <0 || 170 sp->col <0 || sp->col > COLUMNS) 171 pr("ERROR: target is %d,%d\n",sp->line,sp->col); 172 tfield = (sp->col) >> 3; 173 if (sp->line == cursor.line){ 174 if (sp->col > cursor.col)right(sp); 175 else{ 176 distance = (cursor.col -sp->col)*BSlength; 177 if (((TA) && 178 (distance > tfield+((sp->col)&7)*NDlength) 179 ) || 180 (((cursor.col)*NDlength) < distance) 181 ){ 182 cr(); 183 right(sp); 184 } 185 else{ 186 while(cursor.col > sp->col) bs(); 187 } 188 } 189 return; 190 } 191 /*must change row */ 192 if (cursor.col - sp->col > (cursor.col >> 3)){ 193 if (cursor.col == 0)f = 0; 194 else f = -1; 195 } 196 else f = cursor.col >> 3; 197 if (((sp->line << 1) + 1 < cursor.line - f) && (HO != 0)){ 198 /* 199 * home quicker than rlf: 200 * (sp->line + f > cursor.line - sp->line) 201 */ 202 putpad(HO); 203 cursor.col = cursor.line = 0; 204 gto(sp); 205 return; 206 } 207 if (((sp->line << 1) > cursor.line + LINES+1 + f) && (LL != 0)){ 208 /* home,rlf quicker than lf 209 * (LINES+1 - sp->line + f < sp->line - cursor.line) 210 */ 211 if (cursor.line > f + 1){ 212 /* is home faster than wraparound lf? 213 * (cursor.line + 20 - sp->line > 21 - sp->line + f) 214 */ 215 ll(); 216 gto(sp); 217 return; 218 } 219 } 220 if ((LL != 0) && (sp->line > cursor.line + (LINES >> 1) - 1)) 221 cursor.line += LINES; 222 while(sp->line > cursor.line)down(); 223 while(sp->line < cursor.line)up(); 224 gto(sp); /*can recurse since cursor.line = sp->line */ 225 } 226 227 right(sp) 228 struct point *sp; 229 { 230 int field,tfield; 231 int tabcol,strlength; 232 233 if (sp->col < cursor.col) 234 pr("ERROR:right() can't move left\n"); 235 if(TA){ /* If No Tabs: can't send tabs because ttydrive 236 * loses count with control characters. 237 */ 238 field = cursor.col >> 3; 239 /* 240 * This code is useful for a terminal which wraps around on backspaces. 241 * (Mine does.) Unfortunately, this is not specified in termcap, and 242 * most terminals don't work that way. (Of course, most terminals 243 * have addressible cursors, too). 244 */ 245 if (BW && (CM == 0) && 246 ((sp->col << 1) - field > (COLUMNS - 8) << 1 ) 247 ){ 248 if (cursor.line == 0){ 249 outch('\n'); 250 } 251 outch('\r'); 252 cursor.col = COLUMNS + 1; 253 while(cursor.col > sp->col)bs(); 254 if (cursor.line != 0) outch('\n'); 255 return; 256 } 257 258 tfield = sp->col >> 3; 259 260 while (field < tfield){ 261 putpad(TA); 262 cursor.col = ++field << 3; 263 } 264 tabcol = (cursor.col|7) + 1; 265 strlength = (tabcol - sp->col)*BSlength + 1; 266 /* length of sequence to overshoot */ 267 if (((sp->col - cursor.col)*NDlength > strlength) && 268 (tabcol < COLUMNS) 269 ){ 270 /* 271 * Tab past and backup 272 */ 273 putpad(TA); 274 cursor.col = (cursor.col | 7) + 1; 275 while(cursor.col > sp->col)bs(); 276 } 277 } 278 while (sp->col > cursor.col){ 279 nd(); 280 } 281 } 282 283 cr(){ 284 outch('\r'); 285 cursor.col = 0; 286 } 287 288 clear(){ 289 int i; 290 291 if (CL){ 292 putpad(CL); 293 cursor.col=cursor.line=0; 294 } else { 295 for(i=0; i<LINES; i++) { 296 putchar('\n'); 297 } 298 cursor.line = LINES - 1; 299 home(); 300 } 301 } 302 303 home(){ 304 struct point z; 305 306 if(HO != 0){ 307 putpad(HO); 308 cursor.col = cursor.line = 0; 309 return; 310 } 311 z.col = z.line = 0; 312 move(&z); 313 } 314 315 ll(){ 316 int j,l; 317 struct point z; 318 319 l = lcnt + 2; 320 if(LL != NULL && LINES==l){ 321 putpad(LL); 322 cursor.line = LINES-1; 323 cursor.col = 0; 324 return; 325 } 326 z.col = 0; 327 z.line = l-1; 328 move(&z); 329 } 330 331 up(){ 332 putpad(UP); 333 cursor.line--; 334 } 335 336 down(){ 337 putpad(DO); 338 cursor.line++; 339 if (cursor.line >= LINES)cursor.line=LINES-1; 340 } 341 bs(){ 342 if (cursor.col > 0){ 343 putpad(BS); 344 cursor.col--; 345 } 346 } 347 348 nd(){ 349 putpad(ND); 350 cursor.col++; 351 if (cursor.col == COLUMNS+1){ 352 cursor.line++; 353 cursor.col = 0; 354 if (cursor.line >= LINES)cursor.line=LINES-1; 355 } 356 } 357 358 pch(c) 359 { 360 outch(c); 361 if(++cursor.col >= COLUMNS && AM) { 362 cursor.col = 0; 363 ++cursor.line; 364 } 365 } 366 367 void 368 #if __STDC__ 369 apr(struct point *ps, const char *fmt, ...) 370 #else 371 apr(ps, fmt, va_alist) 372 struct point *ps; 373 char *fmt; 374 va_dcl 375 #endif 376 { 377 struct point p; 378 va_list ap; 379 380 p.line = ps->line+1; p.col = ps->col+1; 381 move(&p); 382 #if __STDC__ 383 va_start(ap, fmt); 384 #else 385 va_start(ap); 386 #endif 387 (void)vsprintf(str, fmt, ap); 388 va_end(ap); 389 pstring(str); 390 } 391 392 void 393 #if __STDC__ 394 pr(const char *fmt, ...) 395 #else 396 pr(fmt, va_alist) 397 char *fmt; 398 va_dcl 399 #endif 400 { 401 va_list ap; 402 403 #if __STDC__ 404 va_start(ap, fmt); 405 #else 406 va_start(ap); 407 #endif 408 (void)vsprintf(str, fmt, ap); 409 va_end(ap); 410 pstring(str); 411 } 412 413 pstring(s) 414 char *s;{ 415 struct point z; 416 int stcol; 417 418 stcol = cursor.col; 419 while (s[0] != '\0'){ 420 switch (s[0]){ 421 case '\n': 422 move(point(&z,0,cursor.line+1)); 423 break; 424 case '\r': 425 move(point(&z,stcol,cursor.line+1)); 426 break; 427 case '\t': 428 z.col = (((cursor.col + 8) >> 3) << 3); 429 z.line = cursor.line; 430 move(&z); 431 break; 432 case '\b': 433 bs(); 434 break; 435 case CTRL('g'): 436 outch(CTRL('g')); 437 break; 438 default: 439 if (s[0] < ' ')break; 440 pch(s[0]); 441 } 442 s++; 443 } 444 } 445 446 pchar(ps,ch) 447 struct point *ps; 448 char ch;{ 449 struct point p; 450 p.col = ps->col + 1; p.line = ps->line + 1; 451 if ( 452 (p.col >= 0) && 453 (p.line >= 0) && 454 ( 455 ( 456 (p.line < LINES) && 457 (p.col < COLUMNS) 458 ) || 459 ( 460 (p.col == COLUMNS) && 461 (p.line < LINES-1) 462 ) 463 ) 464 ){ 465 move(&p); 466 pch(ch); 467 } 468 } 469 470 471 outch(c) 472 { 473 putchar(c); 474 } 475 476 putpad(str) 477 char *str; 478 { 479 if (str) 480 tputs(str, 1, outch); 481 } 482 baudrate() 483 { 484 485 switch (orig.sg_ospeed){ 486 case B300: 487 return(300); 488 case B1200: 489 return(1200); 490 case B4800: 491 return(4800); 492 case B9600: 493 return(9600); 494 default: 495 return(0); 496 } 497 } 498 delay(t) 499 int t; 500 { 501 int k,j; 502 503 k = baudrate() * t / 300; 504 for(j=0;j<k;j++){ 505 putchar(PC); 506 } 507 } 508 509 done() 510 { 511 cook(); 512 exit(0); 513 } 514 515 cook() 516 { 517 delay(1); 518 putpad(TE); 519 putpad(KE); 520 fflush(stdout); 521 stty(0, &orig); 522 #ifdef TIOCSLTC 523 ioctl(0, TIOCSLTC, &olttyc); 524 #endif 525 } 526 527 raw() 528 { 529 stty(0, &new); 530 #ifdef TIOCSLTC 531 ioctl(0, TIOCSLTC, &nlttyc); 532 #endif 533 } 534 535 struct point *point(ps,x,y) 536 struct point *ps; 537 int x,y; 538 { 539 ps->col=x; 540 ps->line=y; 541 return(ps); 542 } 543 544 char *ap; 545 546 getcap() 547 { 548 char *getenv(); 549 char *term; 550 char *xPC; 551 struct point z; 552 void stop(); 553 #ifdef TIOCGWINSZ 554 struct winsize win; 555 #endif 556 557 term = getenv("TERM"); 558 if (term==0) { 559 fprintf(stderr, "No TERM in environment\n"); 560 exit(1); 561 } 562 563 switch (tgetent(tbuf, term)) { 564 case -1: 565 fprintf(stderr, "Cannot open termcap file\n"); 566 exit(2); 567 case 0: 568 fprintf(stderr, "%s: unknown terminal", term); 569 exit(3); 570 } 571 572 ap = tcapbuf; 573 574 #ifdef TIOCGWINSZ 575 if (ioctl(0, TIOCGWINSZ, (char *) &win) < 0 || 576 (LINES = win.ws_row) == 0 || (COLUMNS = win.ws_col) == 0) { 577 #endif 578 LINES = tgetnum("li"); 579 COLUMNS = tgetnum("co"); 580 #ifdef TIOCGWINSZ 581 } 582 #endif 583 if (!lcnt) 584 lcnt = LINES - 2; 585 if (!ccnt) 586 ccnt = COLUMNS - 3; 587 588 AM = tgetflag("am"); 589 BW = tgetflag("bw"); 590 591 ND = tgetstr("nd", &ap); 592 UP = tgetstr("up", &ap); 593 594 DO = tgetstr("do", &ap); 595 if (DO == 0) 596 DO = "\n"; 597 598 BS = tgetstr("bc", &ap); 599 if (BS == 0 && tgetflag("bs")) 600 BS = "\b"; 601 if (BS) 602 xBC = *BS; 603 604 TA = tgetstr("ta", &ap); 605 if (TA == 0 && tgetflag("pt")) 606 TA = "\t"; 607 608 HO = tgetstr("ho", &ap); 609 CL = tgetstr("cl", &ap); 610 CM = tgetstr("cm", &ap); 611 LL = tgetstr("ll", &ap); 612 613 KL = tgetstr("kl", &ap); 614 KR = tgetstr("kr", &ap); 615 KU = tgetstr("ku", &ap); 616 KD = tgetstr("kd", &ap); 617 Klength = strlen(KL); 618 /* NOTE: If KL, KR, KU, and KD are not 619 * all the same length, some problems 620 * may arise, since tests are made on 621 * all of them together. 622 */ 623 624 TI = tgetstr("ti", &ap); 625 TE = tgetstr("te", &ap); 626 KS = tgetstr("ks", &ap); 627 KE = tgetstr("ke", &ap); 628 629 xPC = tgetstr("pc", &ap); 630 if (xPC) 631 PC = *xPC; 632 633 NDlength = strlen(ND); 634 BSlength = strlen(BS); 635 if ((CM == 0) && 636 (HO == 0 | UP==0 || BS==0 || ND==0)) { 637 fprintf(stderr, "Terminal must have addressible "); 638 fprintf(stderr, "cursor or home + 4 local motions\n"); 639 exit(5); 640 } 641 if (tgetflag("os")) { 642 fprintf(stderr, "Terminal must not overstrike\n"); 643 exit(5); 644 } 645 if (LINES <= 0 || COLUMNS <= 0) { 646 fprintf(stderr, "Must know the screen size\n"); 647 exit(5); 648 } 649 650 gtty(0, &orig); 651 new=orig; 652 new.sg_flags &= ~(ECHO|CRMOD|ALLDELAY|XTABS); 653 new.sg_flags |= CBREAK; 654 signal(SIGINT,stop); 655 ospeed = orig.sg_ospeed; 656 #ifdef TIOCGLTC 657 ioctl(0, TIOCGLTC, &olttyc); 658 nlttyc = olttyc; 659 nlttyc.t_suspc = '\377'; 660 nlttyc.t_dsuspc = '\377'; 661 #endif 662 raw(); 663 664 if ((orig.sg_flags & XTABS) == XTABS) TA=0; 665 putpad(KS); 666 putpad(TI); 667 point(&cursor,0,LINES-1); 668 } 669