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