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