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