/* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)move.c 8.1 (Berkeley) 7/19/93 * $FreeBSD: src/games/snake/snake/move.c,v 1.5.2.1 2000/08/17 06:21:44 jhb Exp $ * $DragonFly: src/games/snake/snake/Attic/move.c,v 1.3 2004/07/27 07:37:39 asmodai Exp $ */ /************************************************************************* * * MOVE LIBRARY * * This set of subroutines moves a cursor to a predefined * location, independent of the terminal type. If the * terminal has an addressable cursor, it uses it. If * not, it optimizes for tabs (currently) even if you don't * have them. * * At all times the current address of the cursor must be maintained, * and that is available as structure cursor. * * The following calls are allowed: * move(sp) move to point sp. * up() move up one line. * down() move down one line. * bs() move left one space (except column 0). * nd() move right one space(no write). * clear() clear screen. * home() home. * ll() move to lower left corner of screen. * cr() carriage return (no line feed). * pr() just like standard printf, but keeps track * of cursor position. (Uses pstring). * apr() same as printf, but first argument is &point. * (Uses pstring). * pstring(s) output the string of printing characters. * However, '\r' is interpreted to mean return * to column of origination AND do linefeed. * '\n' causes . * putpad(str) calls tputs to output character with proper * padding. * outch() the output routine for a character used by * tputs. It just calls putchar. * pch(ch) output character to screen and update * cursor address (must be a standard * printing character). WILL SCROLL. * pchar(ps,ch) prints one character if it is on the * screen at the specified location; * otherwise, dumps it.(no wrap-around). * * getcap() initializes strings for later calls. * cap(string) outputs the string designated in the termcap * data base. (Should not move the cursor.) * done() returns the terminal to intial state and exits. * * point(&p,x,y) return point set to x,y. * * baudrate() returns the baudrate of the terminal. * delay(t) causes an approximately constant delay * independent of baudrate. * Duration is ~ t/20 seconds. * ******************************************************************************/ #include #include #include #include #include "snake.h" int CMlength; int NDlength; int BSlength; int delaystr[10]; short ospeed; static char str[80]; move(sp) struct point *sp; { int distance; int tabcol,ct; struct point z; if (sp->line <0 || sp->col <0 || sp->col > COLUMNS){ pr("move to [%d,%d]?",sp->line,sp->col); return; } if (sp->line >= LINES){ move(point(&z,sp->col,LINES-1)); while(sp->line-- >= LINES)putchar('\n'); return; } if (CM != 0) { char *cmstr = tgoto(CM, sp->col, sp->line); CMlength = strlen(cmstr); if(cursor.line == sp->line){ distance = sp->col - cursor.col; if(distance == 0)return; /* Already there! */ if(distance > 0){ /* Moving to the right */ if(distance*NDlength < CMlength){ right(sp); return; } if(TA){ ct=sp->col&7; tabcol=(cursor.col|7)+1; do{ ct++; tabcol=(tabcol|7)+1; } while(tabcolcol); if(ctcol < CMlength){ cr(); right(sp); return; } /* No more optimizations on same row. */ } distance = sp->col - cursor.col; distance = distance > 0 ? distance*NDlength : -distance * BSlength; if (distance < 0) pr("ERROR: distance is negative: %d",distance); distance += abs(sp->line - cursor.line); if(distance >= CMlength){ putpad(cmstr); cursor.line = sp->line; cursor.col = sp->col; return; } } /* * If we get here we have a terminal that can't cursor * address but has local motions or one which can cursor * address but can get there quicker with local motions. */ gto(sp); } gto(sp) struct point *sp; { int distance,f,tfield; if (cursor.line > LINES || cursor.line <0 || cursor.col <0 || cursor.col > COLUMNS) pr("ERROR: cursor is at %d,%d\n", cursor.line,cursor.col); if (sp->line > LINES || sp->line <0 || sp->col <0 || sp->col > COLUMNS) pr("ERROR: target is %d,%d\n",sp->line,sp->col); tfield = (sp->col) >> 3; if (sp->line == cursor.line){ if (sp->col > cursor.col)right(sp); else{ distance = (cursor.col -sp->col)*BSlength; if (((TA) && (distance > tfield+((sp->col)&7)*NDlength) ) || (((cursor.col)*NDlength) < distance) ){ cr(); right(sp); } else{ while(cursor.col > sp->col) bs(); } } return; } /*must change row */ if (cursor.col - sp->col > (cursor.col >> 3)){ if (cursor.col == 0)f = 0; else f = -1; } else f = cursor.col >> 3; if (((sp->line << 1) + 1 < cursor.line - f) && (HO != 0)){ /* * home quicker than rlf: * (sp->line + f > cursor.line - sp->line) */ putpad(HO); cursor.col = cursor.line = 0; gto(sp); return; } if (((sp->line << 1) > cursor.line + LINES+1 + f) && (LL != 0)){ /* home,rlf quicker than lf * (LINES+1 - sp->line + f < sp->line - cursor.line) */ if (cursor.line > f + 1){ /* is home faster than wraparound lf? * (cursor.line + 20 - sp->line > 21 - sp->line + f) */ ll(); gto(sp); return; } } if ((LL != 0) && (sp->line > cursor.line + (LINES >> 1) - 1)) cursor.line += LINES; while(sp->line > cursor.line)down(); while(sp->line < cursor.line)up(); gto(sp); /*can recurse since cursor.line = sp->line */ } right(sp) struct point *sp; { int field,tfield; int tabcol,strlength; if (sp->col < cursor.col) pr("ERROR:right() can't move left\n"); if(TA){ /* If No Tabs: can't send tabs because ttydrive * loses count with control characters. */ field = cursor.col >> 3; /* * This code is useful for a terminal which wraps around on backspaces. * (Mine does.) Unfortunately, this is not specified in termcap, and * most terminals don't work that way. (Of course, most terminals * have addressible cursors, too). */ if (BW && (CM == 0) && ((sp->col << 1) - field > (COLUMNS - 8) << 1 ) ){ if (cursor.line == 0){ outch('\n'); } outch('\r'); cursor.col = COLUMNS + 1; while(cursor.col > sp->col)bs(); if (cursor.line != 0) outch('\n'); return; } tfield = sp->col >> 3; while (field < tfield){ putpad(TA); cursor.col = ++field << 3; } tabcol = (cursor.col|7) + 1; strlength = (tabcol - sp->col)*BSlength + 1; /* length of sequence to overshoot */ if (((sp->col - cursor.col)*NDlength > strlength) && (tabcol < COLUMNS) ){ /* * Tab past and backup */ putpad(TA); cursor.col = (cursor.col | 7) + 1; while(cursor.col > sp->col)bs(); } } while (sp->col > cursor.col){ nd(); } } cr(){ outch('\r'); cursor.col = 0; } clear(){ int i; if (CL){ putpad(CL); cursor.col=cursor.line=0; } else { for(i=0; i= LINES)cursor.line=LINES-1; } bs(){ if (cursor.col > 0){ putpad(BS); cursor.col--; } } nd(){ putpad(ND); cursor.col++; if (cursor.col == COLUMNS+1){ cursor.line++; cursor.col = 0; if (cursor.line >= LINES)cursor.line=LINES-1; } } pch(c) { outch(c); if(++cursor.col >= COLUMNS && AM) { cursor.col = 0; ++cursor.line; } } void apr(struct point *ps, const char *fmt, ...) { struct point p; va_list ap; p.line = ps->line+1; p.col = ps->col+1; move(&p); va_start(ap, fmt); (void)vsprintf(str, fmt, ap); va_end(ap); pstring(str); } void pr(const char *fmt, ...) { va_list ap; va_start(ap, fmt); (void)vsprintf(str, fmt, ap); va_end(ap); pstring(str); } pstring(s) char *s;{ struct point z; int stcol; stcol = cursor.col; while (s[0] != '\0'){ switch (s[0]){ case '\n': move(point(&z,0,cursor.line+1)); break; case '\r': move(point(&z,stcol,cursor.line+1)); break; case '\t': z.col = (((cursor.col + 8) >> 3) << 3); z.line = cursor.line; move(&z); break; case '\b': bs(); break; case CTRL('g'): outch(CTRL('g')); break; default: if (s[0] < ' ')break; pch(s[0]); } s++; } } pchar(ps,ch) struct point *ps; char ch;{ struct point p; p.col = ps->col + 1; p.line = ps->line + 1; if ( (p.col >= 0) && (p.line >= 0) && ( ( (p.line < LINES) && (p.col < COLUMNS) ) || ( (p.col == COLUMNS) && (p.line < LINES-1) ) ) ){ move(&p); pch(ch); } } outch(c) { putchar(c); } putpad(str) char *str; { if (str) tputs(str, 1, outch); } #if 0 baudrate() { switch (orig.sg_ospeed){ case B300: return(300); case B1200: return(1200); case B4800: return(4800); case B9600: return(9600); default: return(0); } } #endif delay(t) unsigned int t; { while (usleep(t*50000U) == -1 && errno == EINTR) ; } done() { cook(); exit(0); } cook() { delay(1); putpad(TE); putpad(KE); putpad(VE); fflush(stdout); stty(0, &orig); #ifdef TIOCSLTC ioctl(0, TIOCSLTC, &olttyc); #endif } raw() { stty(0, &new); #ifdef TIOCSLTC ioctl(0, TIOCSLTC, &nlttyc); #endif } struct point *point(ps,x,y) struct point *ps; int x,y; { ps->col=x; ps->line=y; return(ps); } char *ap; getcap() { char *getenv(); char *term; char *xPC; void stop(); #ifdef TIOCGWINSZ struct winsize win; #endif term = getenv("TERM"); if (term==0) { fprintf(stderr, "No TERM in environment\n"); exit(1); } switch (tgetent(tbuf, term)) { case -1: fprintf(stderr, "Cannot open termcap file\n"); exit(2); case 0: fprintf(stderr, "%s: unknown terminal", term); exit(3); } ap = tcapbuf; #ifdef TIOCGWINSZ if (ioctl(0, TIOCGWINSZ, (char *) &win) < 0 || (LINES = win.ws_row) == 0 || (COLUMNS = win.ws_col) == 0) { #endif LINES = tgetnum("li"); COLUMNS = tgetnum("co"); #ifdef TIOCGWINSZ } #endif if (!lcnt) lcnt = LINES - 2; if (!ccnt) ccnt = COLUMNS - 3; AM = tgetflag("am"); BW = tgetflag("bw"); ND = tgetstr("nd", &ap); UP = tgetstr("up", &ap); DO = tgetstr("do", &ap); if (DO == 0) DO = "\n"; BS = tgetstr("bc", &ap); if (BS == 0 && tgetflag("bs")) BS = "\b"; if (BS) xBC = *BS; TA = tgetstr("ta", &ap); if (TA == 0 && tgetflag("pt")) TA = "\t"; HO = tgetstr("ho", &ap); CL = tgetstr("cl", &ap); CM = tgetstr("cm", &ap); LL = tgetstr("ll", &ap); KL = tgetstr("kl", &ap); KR = tgetstr("kr", &ap); KU = tgetstr("ku", &ap); KD = tgetstr("kd", &ap); if (KL) Klength = strlen(KL); else Klength = strlen(KL); /* * NOTE: If KL, KR, KU, and KD are not * all the same length, some problems * may arise, since tests are made on * all of them together. */ TI = tgetstr("ti", &ap); TE = tgetstr("te", &ap); KS = tgetstr("ks", &ap); KE = tgetstr("ke", &ap); VI = tgetstr("vi", &ap); VE = tgetstr("ve", &ap); xPC = tgetstr("pc", &ap); if (xPC) PC = *xPC; if (ND) NDlength = strlen(ND); else NDlength = 0; if (BS) BSlength = strlen(BS); else BSlength = 0; if ((CM == 0) && (HO == 0 || DO == 0 || UP==0 || BS==0 || ND==0)) { /* XXX as written in rev.1.6, we can assert(DO) */ fprintf(stderr, "Terminal must have addressible "); fprintf(stderr, "cursor or home + 4 local motions\n"); exit(5); } if (tgetflag("os")) { fprintf(stderr, "Terminal must not overstrike\n"); exit(5); } if (LINES <= 0 || COLUMNS <= 0) { fprintf(stderr, "Must know the screen size\n"); exit(5); } gtty(0, &orig); new=orig; new.sg_flags &= ~(ECHO|CRMOD|ALLDELAY|XTABS); new.sg_flags |= CBREAK; signal(SIGINT,stop); ospeed = orig.sg_ospeed; #ifdef TIOCGLTC ioctl(0, TIOCGLTC, &olttyc); nlttyc = olttyc; nlttyc.t_suspc = '\377'; nlttyc.t_dsuspc = '\377'; #endif raw(); if ((orig.sg_flags & XTABS) == XTABS) TA=0; putpad(KS); putpad(TI); point(&cursor,0,LINES-1); }