1: /* 2: * Copyright (c) 1980 Regents of the University of California. 3: * All rights reserved. The Berkeley software License Agreement 4: * specifies the terms and conditions for redistribution. 5: */ 6: 7: #if !defined(lint) && defined(DOSCCS) 8: static char sccsid[] = "@(#)move.c 5.1.1 (2.11BSD) 1997/3/28"; 9: #endif 10: 11: /************************************************************************* 12: * 13: * MOVE LIBRARY 14: * 15: * This set of subroutines moves a cursor to a predefined 16: * location, independent of the terminal type. If the 17: * terminal has an addressable cursor, it uses it. If 18: * not, it optimizes for tabs (currently) even if you don't 19: * have them. 20: * 21: * At all times the current address of the cursor must be maintained, 22: * and that is available as structure cursor. 23: * 24: * The following calls are allowed: 25: * move(sp) move to point sp. 26: * up() move up one line. 27: * down() move down one line. 28: * bs() move left one space (except column 0). 29: * nd() move right one space(no write). 30: * clear() clear screen. 31: * home() home. 32: * ll() move to lower left corner of screen. 33: * cr() carriage return (no line feed). 34: * printf() just like standard printf, but keeps track 35: * of cursor position. (Uses pstring). 36: * aprintf() same as printf, but first argument is &point. 37: * (Uses pstring). 38: * pstring(s) output the string of printing characters. 39: * However, '\r' is interpreted to mean return 40: * to column of origination AND do linefeed. 41: * '\n' causes <cr><lf>. 42: * putpad(str) calls tputs to output character with proper 43: * padding. 44: * outch() the output routine for a character used by 45: * tputs. It just calls putchar. 46: * pch(ch) output character to screen and update 47: * cursor address (must be a standard 48: * printing character). WILL SCROLL. 49: * pchar(ps,ch) prints one character if it is on the 50: * screen at the specified location; 51: * otherwise, dumps it.(no wrap-around). 52: * 53: * getcap() initializes strings for later calls. 54: * cap(string) outputs the string designated in the termcap 55: * data base. (Should not move the cursor.) 56: * done(int) returns the terminal to intial state. If int 57: * is not 0, it exits. 58: * 59: * same(&p1,&p2) returns 1 if p1 and p2 are the same point. 60: * point(&p,x,y) return point set to x,y. 61: * 62: * baudrate(x) returns the baudrate of the terminal. 63: * delay(t) causes an approximately constant delay 64: * independent of baudrate. 65: * Duration is ~ t/20 seconds. 66: * 67: ******************************************************************************/ 68: 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: printf("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)printf("ERROR: distance is negative: %d",distance); 138: distance += abs(sp->line - cursor.line); 139: if(distance >= CMlength){ 140: putpad(cmstr); 141: cursor.line = sp->line; 142: cursor.col = sp->col; 143: return; 144: } 145: } 146: 147: /* 148: * If we get here we have a terminal that can't cursor 149: * address but has local motions or one which can cursor 150: * address but can get there quicker with local motions. 151: */ 152: gto(sp); 153: } 154: gto(sp) 155: struct point *sp; 156: { 157: 158: int distance,f,tfield,j; 159: 160: if (cursor.line > LINES || cursor.line <0 || 161: cursor.col <0 || cursor.col > COLUMNS) 162: printf("ERROR: cursor is at %d,%d\n", 163: cursor.line,cursor.col); 164: if (sp->line > LINES || sp->line <0 || 165: sp->col <0 || sp->col > COLUMNS) 166: printf("ERROR: target is %d,%d\n",sp->line,sp->col); 167: tfield = (sp->col) >> 3; 168: if (sp->line == cursor.line){ 169: if (sp->col > cursor.col)right(sp); 170: else{ 171: distance = (cursor.col -sp->col)*BSlength; 172: if (((TA) && 173: (distance > tfield+((sp->col)&7)*NDlength) 174: ) || 175: (((cursor.col)*NDlength) < distance) 176: ){ 177: cr(); 178: right(sp); 179: } 180: else{ 181: while(cursor.col > sp->col) bs(); 182: } 183: } 184: return; 185: } 186: /*must change row */ 187: if (cursor.col - sp->col > (cursor.col >> 3)){ 188: if (cursor.col == 0)f = 0; 189: else f = -1; 190: } 191: else f = cursor.col >> 3; 192: if (((sp->line << 1) + 1 < cursor.line - f) && (HO != 0)){ 193: /* 194: * home quicker than rlf: 195: * (sp->line + f > cursor.line - sp->line) 196: */ 197: putpad(HO); 198: cursor.col = cursor.line = 0; 199: gto(sp); 200: return; 201: } 202: if (((sp->line << 1) > cursor.line + LINES+1 + f) && (LL != 0)){ 203: /* home,rlf quicker than lf 204: * (LINES+1 - sp->line + f < sp->line - cursor.line) 205: */ 206: if (cursor.line > f + 1){ 207: /* is home faster than wraparound lf? 208: * (cursor.line + 20 - sp->line > 21 - sp->line + f) 209: */ 210: ll(); 211: gto(sp); 212: return; 213: } 214: } 215: if ((LL != 0) && (sp->line > cursor.line + (LINES >> 1) - 1)) 216: cursor.line += LINES; 217: while(sp->line > cursor.line)down(); 218: while(sp->line < cursor.line)up(); 219: gto(sp); /*can recurse since cursor.line = sp->line */ 220: } 221: 222: right(sp) 223: struct point *sp; 224: { 225: int field,tfield; 226: int tabcol,strlength; 227: 228: if (sp->col < cursor.col) 229: printf("ERROR:right() can't move left\n"); 230: if(TA){ /* If No Tabs: can't send tabs because ttydrive 231: * loses count with control characters. 232: */ 233: field = cursor.col >> 3; 234: /* 235: * This code is useful for a terminal which wraps around on backspaces. 236: * (Mine does.) Unfortunately, this is not specified in termcap, and 237: * most terminals don't work that way. (Of course, most terminals 238: * have addressible cursors, too). 239: */ 240: if (BW && (CM == 0) && 241: ((sp->col << 1) - field > (COLUMNS - 8) << 1 ) 242: ){ 243: if (cursor.line == 0){ 244: outch('\n'); 245: } 246: outch('\r'); 247: cursor.col = COLUMNS + 1; 248: while(cursor.col > sp->col)bs(); 249: if (cursor.line != 0) outch('\n'); 250: return; 251: } 252: 253: tfield = sp->col >> 3; 254: 255: while (field < tfield){ 256: putpad(TA); 257: cursor.col = ++field << 3; 258: } 259: tabcol = (cursor.col|7) + 1; 260: strlength = (tabcol - sp->col)*BSlength + 1; 261: /* length of sequence to overshoot */ 262: if (((sp->col - cursor.col)*NDlength > strlength) && 263: (tabcol < COLUMNS) 264: ){ 265: /* 266: * Tab past and backup 267: */ 268: putpad(TA); 269: cursor.col = (cursor.col | 7) + 1; 270: while(cursor.col > sp->col)bs(); 271: } 272: } 273: while (sp->col > cursor.col){ 274: nd(); 275: } 276: } 277: 278: cr(){ 279: outch('\r'); 280: cursor.col = 0; 281: } 282: 283: clear(){ 284: int i; 285: 286: if (CL){ 287: putpad(CL); 288: cursor.col=cursor.line=0; 289: } else { 290: for(i=0; i<LINES; i++) { 291: putchar('\n'); 292: } 293: cursor.line = LINES - 1; 294: home(); 295: } 296: } 297: 298: home(){ 299: struct point z; 300: 301: if(HO != 0){ 302: putpad(HO); 303: cursor.col = cursor.line = 0; 304: return; 305: } 306: z.col = z.line = 0; 307: move(&z); 308: } 309: 310: ll(){ 311: int j,l; 312: struct point z; 313: 314: l = lcnt + 2; 315: if(LL != NULL && LINES==l){ 316: putpad(LL); 317: cursor.line = LINES-1; 318: cursor.col = 0; 319: return; 320: } 321: z.col = 0; 322: z.line = l-1; 323: move(&z); 324: } 325: 326: up(){ 327: putpad(UP); 328: cursor.line--; 329: } 330: 331: down(){ 332: putpad(DO); 333: cursor.line++; 334: if (cursor.line >= LINES)cursor.line=LINES-1; 335: } 336: bs(){ 337: if (cursor.col > 0){ 338: putpad(BS); 339: cursor.col--; 340: } 341: } 342: 343: nd(){ 344: putpad(ND); 345: cursor.col++; 346: if (cursor.col == COLUMNS+1){ 347: cursor.line++; 348: cursor.col = 0; 349: if (cursor.line >= LINES)cursor.line=LINES-1; 350: } 351: } 352: 353: pch(c) 354: { 355: outch(c); 356: if(++cursor.col >= COLUMNS && AM) { 357: cursor.col = 0; 358: ++cursor.line; 359: } 360: } 361: 362: aprintf(ps,st,v0,v1,v2,v3,v4,v5,v6,v7,v8,v9) 363: struct point *ps; 364: char *st; 365: int v0,v1,v2,v3,v4,v5,v6,v7,v8,v9; 366: 367: { 368: struct point p; 369: 370: p.line = ps->line+1; p.col = ps->col+1; 371: move(&p); 372: sprintf(str,st,v0,v1,v2,v3,v4,v5,v6,v7,v8,v9); 373: pstring(str); 374: } 375: 376: printf(st,v0,v1,v2,v3,v4,v5,v6,v7,v8,v9) 377: char *st; 378: int v0,v1,v2,v3,v4,v5,v6,v7,v8,v9; 379: { 380: sprintf(str,st,v0,v1,v2,v3,v4,v5,v6,v7,v8,v9); 381: pstring(str); 382: } 383: 384: pstring(s) 385: char *s;{ 386: struct point z; 387: int stcol; 388: 389: stcol = cursor.col; 390: while (s[0] != '\0'){ 391: switch (s[0]){ 392: case '\n': 393: move(point(&z,0,cursor.line+1)); 394: break; 395: case '\r': 396: move(point(&z,stcol,cursor.line+1)); 397: break; 398: case '\t': 399: z.col = (((cursor.col + 8) >> 3) << 3); 400: z.line = cursor.line; 401: move(&z); 402: break; 403: case '\b': 404: bs(); 405: break; 406: case CTRL(g): 407: outch(CTRL(g)); 408: break; 409: default: 410: if (s[0] < ' ')break; 411: pch(s[0]); 412: } 413: s++; 414: } 415: } 416: 417: pchar(ps,ch) 418: struct point *ps; 419: char ch;{ 420: struct point p; 421: p.col = ps->col + 1; p.line = ps->line + 1; 422: if ( 423: (p.col >= 0) && 424: (p.line >= 0) && 425: ( 426: ( 427: (p.line < LINES) && 428: (p.col < COLUMNS) 429: ) || 430: ( 431: (p.col == COLUMNS) && 432: (p.line < LINES-1) 433: ) 434: ) 435: ){ 436: move(&p); 437: pch(ch); 438: } 439: } 440: 441: 442: outch(c) 443: { 444: putchar(c); 445: } 446: 447: putpad(str) 448: char *str; 449: { 450: if (str) 451: tputs(str, 1, outch); 452: } 453: baudrate() 454: { 455: 456: switch (orig.sg_ospeed){ 457: case B300: 458: return(300); 459: case B1200: 460: return(1200); 461: case B4800: 462: return(4800); 463: case B9600: 464: return(9600); 465: default: 466: return(0); 467: } 468: } 469: delay(t) 470: int t; 471: { 472: int k,j; 473: 474: k = baudrate() * t / 300; 475: for(j=0;j<k;j++){ 476: putchar(PC); 477: } 478: } 479: 480: done() 481: { 482: cook(); 483: exit(0); 484: } 485: 486: cook() 487: { 488: delay(1); 489: putpad(TE); 490: putpad(KE); 491: fflush(stdout); 492: stty(0, &orig); 493: #ifdef TIOCSLTC 494: ioctl(0, TIOCSLTC, &olttyc); 495: #endif 496: } 497: 498: raw() 499: { 500: stty(0, &new); 501: #ifdef TIOCSLTC 502: ioctl(0, TIOCSLTC, &nlttyc); 503: #endif 504: } 505: 506: same(sp1,sp2) 507: struct point *sp1, *sp2; 508: { 509: if ((sp1->line == sp2->line) && (sp1->col == sp2->col))return(1); 510: return(0); 511: } 512: 513: struct point *point(ps,x,y) 514: struct point *ps; 515: int x,y; 516: { 517: ps->col=x; 518: ps->line=y; 519: return(ps); 520: } 521: 522: char *ap; 523: 524: getcap() 525: { 526: char *getenv(); 527: char *term; 528: char *xPC; 529: struct point z; 530: int stop(); 531: 532: term = getenv("TERM"); 533: if (term==0) { 534: fprintf(stderr, "No TERM in environment\n"); 535: exit(1); 536: } 537: 538: switch (tgetent(tbuf, term)) { 539: case -1: 540: fprintf(stderr, "Cannot open termcap file\n"); 541: exit(2); 542: case 0: 543: fprintf(stderr, "%s: unknown terminal", term); 544: exit(3); 545: } 546: 547: ap = tcapbuf; 548: 549: LINES = tgetnum("li"); 550: COLUMNS = tgetnum("co"); 551: lcnt = LINES; 552: ccnt = COLUMNS - 1; 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|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: }