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