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.7 (Berkeley) 06/01/90"; 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 * printf() just like standard printf, but keeps track 36 * of cursor position. (Uses pstring). 37 * aprintf() 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 #include "snake.h" 69 70 int CMlength; 71 int NDlength; 72 int BSlength; 73 int delaystr[10]; 74 short ospeed; 75 76 static char str[80]; 77 78 move(sp) 79 struct point *sp; 80 { 81 int distance; 82 int tabcol,ct; 83 struct point z; 84 85 if (sp->line <0 || sp->col <0 || sp->col > COLUMNS){ 86 printf("move to [%d,%d]?",sp->line,sp->col); 87 return; 88 } 89 if (sp->line >= LINES){ 90 move(point(&z,sp->col,LINES-1)); 91 while(sp->line-- >= LINES)putchar('\n'); 92 return; 93 } 94 95 if (CM != 0) { 96 char *cmstr = tgoto(CM, sp->col, sp->line); 97 98 CMlength = strlen(cmstr); 99 if(cursor.line == sp->line){ 100 distance = sp->col - cursor.col; 101 if(distance == 0)return; /* Already there! */ 102 if(distance > 0){ /* Moving to the right */ 103 if(distance*NDlength < CMlength){ 104 right(sp); 105 return; 106 } 107 if(TA){ 108 ct=sp->col&7; 109 tabcol=(cursor.col|7)+1; 110 do{ 111 ct++; 112 tabcol=(tabcol|7)+1; 113 } 114 while(tabcol<sp->col); 115 if(ct<CMlength){ 116 right(sp); 117 return; 118 } 119 } 120 } else { /* Moving to the left */ 121 if (-distance*BSlength < CMlength){ 122 gto(sp); 123 return; 124 } 125 } 126 if(sp->col < CMlength){ 127 cr(); 128 right(sp); 129 return; 130 } 131 /* No more optimizations on same row. */ 132 } 133 distance = sp->col - cursor.col; 134 distance = distance > 0 ? 135 distance*NDlength : -distance * BSlength; 136 if(distance < 0)printf("ERROR: distance is negative: %d",distance); 137 distance += abs(sp->line - cursor.line); 138 if(distance >= CMlength){ 139 putpad(cmstr); 140 cursor.line = sp->line; 141 cursor.col = sp->col; 142 return; 143 } 144 } 145 146 /* 147 * If we get here we have a terminal that can't cursor 148 * address but has local motions or one which can cursor 149 * address but can get there quicker with local motions. 150 */ 151 gto(sp); 152 } 153 gto(sp) 154 struct point *sp; 155 { 156 157 int distance,f,tfield,j; 158 159 if (cursor.line > LINES || cursor.line <0 || 160 cursor.col <0 || cursor.col > COLUMNS) 161 printf("ERROR: cursor is at %d,%d\n", 162 cursor.line,cursor.col); 163 if (sp->line > LINES || sp->line <0 || 164 sp->col <0 || sp->col > COLUMNS) 165 printf("ERROR: target is %d,%d\n",sp->line,sp->col); 166 tfield = (sp->col) >> 3; 167 if (sp->line == cursor.line){ 168 if (sp->col > cursor.col)right(sp); 169 else{ 170 distance = (cursor.col -sp->col)*BSlength; 171 if (((TA) && 172 (distance > tfield+((sp->col)&7)*NDlength) 173 ) || 174 (((cursor.col)*NDlength) < distance) 175 ){ 176 cr(); 177 right(sp); 178 } 179 else{ 180 while(cursor.col > sp->col) bs(); 181 } 182 } 183 return; 184 } 185 /*must change row */ 186 if (cursor.col - sp->col > (cursor.col >> 3)){ 187 if (cursor.col == 0)f = 0; 188 else f = -1; 189 } 190 else f = cursor.col >> 3; 191 if (((sp->line << 1) + 1 < cursor.line - f) && (HO != 0)){ 192 /* 193 * home quicker than rlf: 194 * (sp->line + f > cursor.line - sp->line) 195 */ 196 putpad(HO); 197 cursor.col = cursor.line = 0; 198 gto(sp); 199 return; 200 } 201 if (((sp->line << 1) > cursor.line + LINES+1 + f) && (LL != 0)){ 202 /* home,rlf quicker than lf 203 * (LINES+1 - sp->line + f < sp->line - cursor.line) 204 */ 205 if (cursor.line > f + 1){ 206 /* is home faster than wraparound lf? 207 * (cursor.line + 20 - sp->line > 21 - sp->line + f) 208 */ 209 ll(); 210 gto(sp); 211 return; 212 } 213 } 214 if ((LL != 0) && (sp->line > cursor.line + (LINES >> 1) - 1)) 215 cursor.line += LINES; 216 while(sp->line > cursor.line)down(); 217 while(sp->line < cursor.line)up(); 218 gto(sp); /*can recurse since cursor.line = sp->line */ 219 } 220 221 right(sp) 222 struct point *sp; 223 { 224 int field,tfield; 225 int tabcol,strlength; 226 227 if (sp->col < cursor.col) 228 printf("ERROR:right() can't move left\n"); 229 if(TA){ /* If No Tabs: can't send tabs because ttydrive 230 * loses count with control characters. 231 */ 232 field = cursor.col >> 3; 233 /* 234 * This code is useful for a terminal which wraps around on backspaces. 235 * (Mine does.) Unfortunately, this is not specified in termcap, and 236 * most terminals don't work that way. (Of course, most terminals 237 * have addressible cursors, too). 238 */ 239 if (BW && (CM == 0) && 240 ((sp->col << 1) - field > (COLUMNS - 8) << 1 ) 241 ){ 242 if (cursor.line == 0){ 243 outch('\n'); 244 } 245 outch('\r'); 246 cursor.col = COLUMNS + 1; 247 while(cursor.col > sp->col)bs(); 248 if (cursor.line != 0) outch('\n'); 249 return; 250 } 251 252 tfield = sp->col >> 3; 253 254 while (field < tfield){ 255 putpad(TA); 256 cursor.col = ++field << 3; 257 } 258 tabcol = (cursor.col|7) + 1; 259 strlength = (tabcol - sp->col)*BSlength + 1; 260 /* length of sequence to overshoot */ 261 if (((sp->col - cursor.col)*NDlength > strlength) && 262 (tabcol < COLUMNS) 263 ){ 264 /* 265 * Tab past and backup 266 */ 267 putpad(TA); 268 cursor.col = (cursor.col | 7) + 1; 269 while(cursor.col > sp->col)bs(); 270 } 271 } 272 while (sp->col > cursor.col){ 273 nd(); 274 } 275 } 276 277 cr(){ 278 outch('\r'); 279 cursor.col = 0; 280 } 281 282 clear(){ 283 int i; 284 285 if (CL){ 286 putpad(CL); 287 cursor.col=cursor.line=0; 288 } else { 289 for(i=0; i<LINES; i++) { 290 putchar('\n'); 291 } 292 cursor.line = LINES - 1; 293 home(); 294 } 295 } 296 297 home(){ 298 struct point z; 299 300 if(HO != 0){ 301 putpad(HO); 302 cursor.col = cursor.line = 0; 303 return; 304 } 305 z.col = z.line = 0; 306 move(&z); 307 } 308 309 ll(){ 310 int j,l; 311 struct point z; 312 313 l = lcnt + 2; 314 if(LL != NULL && LINES==l){ 315 putpad(LL); 316 cursor.line = LINES-1; 317 cursor.col = 0; 318 return; 319 } 320 z.col = 0; 321 z.line = l-1; 322 move(&z); 323 } 324 325 up(){ 326 putpad(UP); 327 cursor.line--; 328 } 329 330 down(){ 331 putpad(DO); 332 cursor.line++; 333 if (cursor.line >= LINES)cursor.line=LINES-1; 334 } 335 bs(){ 336 if (cursor.col > 0){ 337 putpad(BS); 338 cursor.col--; 339 } 340 } 341 342 nd(){ 343 putpad(ND); 344 cursor.col++; 345 if (cursor.col == COLUMNS+1){ 346 cursor.line++; 347 cursor.col = 0; 348 if (cursor.line >= LINES)cursor.line=LINES-1; 349 } 350 } 351 352 pch(c) 353 { 354 outch(c); 355 if(++cursor.col >= COLUMNS && AM) { 356 cursor.col = 0; 357 ++cursor.line; 358 } 359 } 360 361 aprintf(ps,st,v0,v1,v2,v3,v4,v5,v6,v7,v8,v9) 362 struct point *ps; 363 char *st; 364 int v0,v1,v2,v3,v4,v5,v6,v7,v8,v9; 365 366 { 367 struct point p; 368 369 p.line = ps->line+1; p.col = ps->col+1; 370 move(&p); 371 (void)sprintf(str,st,v0,v1,v2,v3,v4,v5,v6,v7,v8,v9); 372 pstring(str); 373 } 374 375 printf(st,v0,v1,v2,v3,v4,v5,v6,v7,v8,v9) 376 char *st; 377 int v0,v1,v2,v3,v4,v5,v6,v7,v8,v9; 378 { 379 (void)sprintf(str,st,v0,v1,v2,v3,v4,v5,v6,v7,v8,v9); 380 pstring(str); 381 } 382 383 pstring(s) 384 char *s;{ 385 struct point z; 386 int stcol; 387 388 stcol = cursor.col; 389 while (s[0] != '\0'){ 390 switch (s[0]){ 391 case '\n': 392 move(point(&z,0,cursor.line+1)); 393 break; 394 case '\r': 395 move(point(&z,stcol,cursor.line+1)); 396 break; 397 case '\t': 398 z.col = (((cursor.col + 8) >> 3) << 3); 399 z.line = cursor.line; 400 move(&z); 401 break; 402 case '\b': 403 bs(); 404 break; 405 case CTRL('g'): 406 outch(CTRL('g')); 407 break; 408 default: 409 if (s[0] < ' ')break; 410 pch(s[0]); 411 } 412 s++; 413 } 414 } 415 416 pchar(ps,ch) 417 struct point *ps; 418 char ch;{ 419 struct point p; 420 p.col = ps->col + 1; p.line = ps->line + 1; 421 if ( 422 (p.col >= 0) && 423 (p.line >= 0) && 424 ( 425 ( 426 (p.line < LINES) && 427 (p.col < COLUMNS) 428 ) || 429 ( 430 (p.col == COLUMNS) && 431 (p.line < LINES-1) 432 ) 433 ) 434 ){ 435 move(&p); 436 pch(ch); 437 } 438 } 439 440 441 outch(c) 442 { 443 putchar(c); 444 } 445 446 putpad(str) 447 char *str; 448 { 449 if (str) 450 tputs(str, 1, outch); 451 } 452 baudrate() 453 { 454 455 switch (orig.sg_ospeed){ 456 case B300: 457 return(300); 458 case B1200: 459 return(1200); 460 case B4800: 461 return(4800); 462 case B9600: 463 return(9600); 464 default: 465 return(0); 466 } 467 } 468 delay(t) 469 int t; 470 { 471 int k,j; 472 473 k = baudrate() * t / 300; 474 for(j=0;j<k;j++){ 475 putchar(PC); 476 } 477 } 478 479 done() 480 { 481 cook(); 482 exit(0); 483 } 484 485 cook() 486 { 487 delay(1); 488 putpad(TE); 489 putpad(KE); 490 fflush(stdout); 491 stty(0, &orig); 492 #ifdef TIOCSLTC 493 ioctl(0, TIOCSLTC, &olttyc); 494 #endif 495 } 496 497 raw() 498 { 499 stty(0, &new); 500 #ifdef TIOCSLTC 501 ioctl(0, TIOCSLTC, &nlttyc); 502 #endif 503 } 504 505 struct point *point(ps,x,y) 506 struct point *ps; 507 int x,y; 508 { 509 ps->col=x; 510 ps->line=y; 511 return(ps); 512 } 513 514 char *ap; 515 516 getcap() 517 { 518 char *getenv(); 519 char *term; 520 char *xPC; 521 struct point z; 522 int stop(); 523 524 term = getenv("TERM"); 525 if (term==0) { 526 fprintf(stderr, "No TERM in environment\n"); 527 exit(1); 528 } 529 530 switch (tgetent(tbuf, term)) { 531 case -1: 532 fprintf(stderr, "Cannot open termcap file\n"); 533 exit(2); 534 case 0: 535 fprintf(stderr, "%s: unknown terminal", term); 536 exit(3); 537 } 538 539 ap = tcapbuf; 540 541 LINES = tgetnum("li"); 542 COLUMNS = tgetnum("co"); 543 if (!lcnt) 544 lcnt = LINES - 2; 545 if (!ccnt) 546 ccnt = COLUMNS - 3; 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