1 /* 2 * Copyright (c) 1980 Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char sccsid[] = "@(#)move.c 5.9 (Berkeley) 02/16/92"; 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 #if __STDC__ 368 apr(struct point *ps, char *fmt, ...) 369 #else 370 apr(ps, fmt, va_alist) 371 struct point *ps; 372 char *fmt; 373 va_dcl 374 #endif 375 { 376 struct point p; 377 va_list ap; 378 379 p.line = ps->line+1; p.col = ps->col+1; 380 move(&p); 381 #if __STDC__ 382 va_start(ap, fmt); 383 #else 384 va_start(ap); 385 #endif 386 (void)vsprintf(str, fmt, ap); 387 va_end(ap); 388 pstring(str); 389 } 390 391 #if __STDC__ 392 pr(char *fmt, ...) 393 #else 394 pr(fmt, va_alist) 395 char *fmt; 396 va_dcl 397 #endif 398 { 399 va_list ap; 400 401 #if __STDC__ 402 va_start(ap, fmt); 403 #else 404 va_start(ap); 405 #endif 406 (void)vsprintf(str, fmt, ap); 407 va_end(ap); 408 pstring(str); 409 } 410 411 pstring(s) 412 char *s;{ 413 struct point z; 414 int stcol; 415 416 stcol = cursor.col; 417 while (s[0] != '\0'){ 418 switch (s[0]){ 419 case '\n': 420 move(point(&z,0,cursor.line+1)); 421 break; 422 case '\r': 423 move(point(&z,stcol,cursor.line+1)); 424 break; 425 case '\t': 426 z.col = (((cursor.col + 8) >> 3) << 3); 427 z.line = cursor.line; 428 move(&z); 429 break; 430 case '\b': 431 bs(); 432 break; 433 case CTRL('g'): 434 outch(CTRL('g')); 435 break; 436 default: 437 if (s[0] < ' ')break; 438 pch(s[0]); 439 } 440 s++; 441 } 442 } 443 444 pchar(ps,ch) 445 struct point *ps; 446 char ch;{ 447 struct point p; 448 p.col = ps->col + 1; p.line = ps->line + 1; 449 if ( 450 (p.col >= 0) && 451 (p.line >= 0) && 452 ( 453 ( 454 (p.line < LINES) && 455 (p.col < COLUMNS) 456 ) || 457 ( 458 (p.col == COLUMNS) && 459 (p.line < LINES-1) 460 ) 461 ) 462 ){ 463 move(&p); 464 pch(ch); 465 } 466 } 467 468 469 outch(c) 470 { 471 putchar(c); 472 } 473 474 putpad(str) 475 char *str; 476 { 477 if (str) 478 tputs(str, 1, outch); 479 } 480 baudrate() 481 { 482 483 switch (orig.sg_ospeed){ 484 case B300: 485 return(300); 486 case B1200: 487 return(1200); 488 case B4800: 489 return(4800); 490 case B9600: 491 return(9600); 492 default: 493 return(0); 494 } 495 } 496 delay(t) 497 int t; 498 { 499 int k,j; 500 501 k = baudrate() * t / 300; 502 for(j=0;j<k;j++){ 503 putchar(PC); 504 } 505 } 506 507 done() 508 { 509 cook(); 510 exit(0); 511 } 512 513 cook() 514 { 515 delay(1); 516 putpad(TE); 517 putpad(KE); 518 fflush(stdout); 519 stty(0, &orig); 520 #ifdef TIOCSLTC 521 ioctl(0, TIOCSLTC, &olttyc); 522 #endif 523 } 524 525 raw() 526 { 527 stty(0, &new); 528 #ifdef TIOCSLTC 529 ioctl(0, TIOCSLTC, &nlttyc); 530 #endif 531 } 532 533 struct point *point(ps,x,y) 534 struct point *ps; 535 int x,y; 536 { 537 ps->col=x; 538 ps->line=y; 539 return(ps); 540 } 541 542 char *ap; 543 544 getcap() 545 { 546 char *getenv(); 547 char *term; 548 char *xPC; 549 struct point z; 550 void stop(); 551 552 term = getenv("TERM"); 553 if (term==0) { 554 fprintf(stderr, "No TERM in environment\n"); 555 exit(1); 556 } 557 558 switch (tgetent(tbuf, term)) { 559 case -1: 560 fprintf(stderr, "Cannot open termcap file\n"); 561 exit(2); 562 case 0: 563 fprintf(stderr, "%s: unknown terminal", term); 564 exit(3); 565 } 566 567 ap = tcapbuf; 568 569 LINES = tgetnum("li"); 570 COLUMNS = tgetnum("co"); 571 if (!lcnt) 572 lcnt = LINES - 2; 573 if (!ccnt) 574 ccnt = COLUMNS - 3; 575 576 AM = tgetflag("am"); 577 BW = tgetflag("bw"); 578 579 ND = tgetstr("nd", &ap); 580 UP = tgetstr("up", &ap); 581 582 DO = tgetstr("do", &ap); 583 if (DO == 0) 584 DO = "\n"; 585 586 BS = tgetstr("bc", &ap); 587 if (BS == 0 && tgetflag("bs")) 588 BS = "\b"; 589 if (BS) 590 xBC = *BS; 591 592 TA = tgetstr("ta", &ap); 593 if (TA == 0 && tgetflag("pt")) 594 TA = "\t"; 595 596 HO = tgetstr("ho", &ap); 597 CL = tgetstr("cl", &ap); 598 CM = tgetstr("cm", &ap); 599 LL = tgetstr("ll", &ap); 600 601 KL = tgetstr("kl", &ap); 602 KR = tgetstr("kr", &ap); 603 KU = tgetstr("ku", &ap); 604 KD = tgetstr("kd", &ap); 605 Klength = strlen(KL); 606 /* NOTE: If KL, KR, KU, and KD are not 607 * all the same length, some problems 608 * may arise, since tests are made on 609 * all of them together. 610 */ 611 612 TI = tgetstr("ti", &ap); 613 TE = tgetstr("te", &ap); 614 KS = tgetstr("ks", &ap); 615 KE = tgetstr("ke", &ap); 616 617 xPC = tgetstr("pc", &ap); 618 if (xPC) 619 PC = *xPC; 620 621 NDlength = strlen(ND); 622 BSlength = strlen(BS); 623 if ((CM == 0) && 624 (HO == 0 | UP==0 || BS==0 || ND==0)) { 625 fprintf(stderr, "Terminal must have addressible "); 626 fprintf(stderr, "cursor or home + 4 local motions\n"); 627 exit(5); 628 } 629 if (tgetflag("os")) { 630 fprintf(stderr, "Terminal must not overstrike\n"); 631 exit(5); 632 } 633 if (LINES <= 0 || COLUMNS <= 0) { 634 fprintf(stderr, "Must know the screen size\n"); 635 exit(5); 636 } 637 638 gtty(0, &orig); 639 new=orig; 640 new.sg_flags &= ~(ECHO|CRMOD|ALLDELAY|XTABS); 641 new.sg_flags |= CBREAK; 642 signal(SIGINT,stop); 643 ospeed = orig.sg_ospeed; 644 #ifdef TIOCGLTC 645 ioctl(0, TIOCGLTC, &olttyc); 646 nlttyc = olttyc; 647 nlttyc.t_suspc = '\377'; 648 nlttyc.t_dsuspc = '\377'; 649 #endif 650 raw(); 651 652 if ((orig.sg_flags & XTABS) == XTABS) TA=0; 653 putpad(KS); 654 putpad(TI); 655 point(&cursor,0,LINES-1); 656 } 657