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