xref: /original-bsd/games/snake/snake/move.c (revision 3b6250d9)
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.9 (Berkeley) 02/16/92";
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 #if __STDC__
368 apr(struct point *ps, char *fmt, ...)
369 #else
370 apr(ps, fmt, va_alist)
371 	struct point *ps;
372 	char *fmt;
373 	va_dcl
374 #endif
375 {
376 	struct point p;
377 	va_list ap;
378 
379 	p.line = ps->line+1; p.col = ps->col+1;
380 	move(&p);
381 #if __STDC__
382 	va_start(ap, fmt);
383 #else
384 	va_start(ap);
385 #endif
386 	(void)vsprintf(str, fmt, ap);
387 	va_end(ap);
388 	pstring(str);
389 }
390 
391 #if __STDC__
392 pr(char *fmt, ...)
393 #else
394 pr(fmt, va_alist)
395 	char *fmt;
396 	va_dcl
397 #endif
398 {
399 	va_list ap;
400 
401 #if __STDC__
402 	va_start(ap, fmt);
403 #else
404 	va_start(ap);
405 #endif
406 	(void)vsprintf(str, fmt, ap);
407 	va_end(ap);
408 	pstring(str);
409 }
410 
411 pstring(s)
412 char *s;{
413 	struct point z;
414 	int stcol;
415 
416 	stcol = cursor.col;
417 	while (s[0] != '\0'){
418 		switch (s[0]){
419 		case '\n':
420 			move(point(&z,0,cursor.line+1));
421 			break;
422 		case '\r':
423 			move(point(&z,stcol,cursor.line+1));
424 			break;
425 		case '\t':
426 			z.col = (((cursor.col + 8) >> 3) << 3);
427 			z.line = cursor.line;
428 			move(&z);
429 			break;
430 		case '\b':
431 			bs();
432 			break;
433 		case CTRL('g'):
434 			outch(CTRL('g'));
435 			break;
436 		default:
437 			if (s[0] < ' ')break;
438 			pch(s[0]);
439 		}
440 		s++;
441 	}
442 }
443 
444 pchar(ps,ch)
445 struct point *ps;
446 char ch;{
447 	struct point p;
448 	p.col = ps->col + 1; p.line = ps->line + 1;
449 	if (
450 		(p.col >= 0) &&
451 		(p.line >= 0) &&
452 		(
453 			(
454 				(p.line < LINES) &&
455 				(p.col < COLUMNS)
456 			) ||
457 			(
458 	    			(p.col == COLUMNS) &&
459 				(p.line < LINES-1)
460 			)
461 	  	)
462 	){
463 		move(&p);
464 		pch(ch);
465 	}
466 }
467 
468 
469 outch(c)
470 {
471 	putchar(c);
472 }
473 
474 putpad(str)
475 char *str;
476 {
477 	if (str)
478 		tputs(str, 1, outch);
479 }
480 baudrate()
481 {
482 
483 	switch (orig.sg_ospeed){
484 	case B300:
485 		return(300);
486 	case B1200:
487 		return(1200);
488 	case B4800:
489 		return(4800);
490 	case B9600:
491 		return(9600);
492 	default:
493 		return(0);
494 	}
495 }
496 delay(t)
497 int t;
498 {
499 	int k,j;
500 
501 	k = baudrate() * t / 300;
502 	for(j=0;j<k;j++){
503 		putchar(PC);
504 	}
505 }
506 
507 done()
508 {
509 	cook();
510 	exit(0);
511 }
512 
513 cook()
514 {
515 	delay(1);
516 	putpad(TE);
517 	putpad(KE);
518 	fflush(stdout);
519 	stty(0, &orig);
520 #ifdef TIOCSLTC
521 	ioctl(0, TIOCSLTC, &olttyc);
522 #endif
523 }
524 
525 raw()
526 {
527 	stty(0, &new);
528 #ifdef TIOCSLTC
529 	ioctl(0, TIOCSLTC, &nlttyc);
530 #endif
531 }
532 
533 struct point *point(ps,x,y)
534 struct point *ps;
535 int x,y;
536 {
537 	ps->col=x;
538 	ps->line=y;
539 	return(ps);
540 }
541 
542 char *ap;
543 
544 getcap()
545 {
546 	char *getenv();
547 	char *term;
548 	char *xPC;
549 	struct point z;
550 	void stop();
551 
552 	term = getenv("TERM");
553 	if (term==0) {
554 		fprintf(stderr, "No TERM in environment\n");
555 		exit(1);
556 	}
557 
558 	switch (tgetent(tbuf, term)) {
559 	case -1:
560 		fprintf(stderr, "Cannot open termcap file\n");
561 		exit(2);
562 	case 0:
563 		fprintf(stderr, "%s: unknown terminal", term);
564 		exit(3);
565 	}
566 
567 	ap = tcapbuf;
568 
569 	LINES = tgetnum("li");
570 	COLUMNS = tgetnum("co");
571 	if (!lcnt)
572 		lcnt = LINES - 2;
573 	if (!ccnt)
574 		ccnt = COLUMNS - 3;
575 
576 	AM = tgetflag("am");
577 	BW = tgetflag("bw");
578 
579 	ND = tgetstr("nd", &ap);
580 	UP = tgetstr("up", &ap);
581 
582 	DO = tgetstr("do", &ap);
583 	if (DO == 0)
584 		DO = "\n";
585 
586 	BS = tgetstr("bc", &ap);
587 	if (BS == 0 && tgetflag("bs"))
588 		BS = "\b";
589 	if (BS)
590 		xBC = *BS;
591 
592 	TA = tgetstr("ta", &ap);
593 	if (TA == 0 && tgetflag("pt"))
594 		TA = "\t";
595 
596 	HO = tgetstr("ho", &ap);
597 	CL = tgetstr("cl", &ap);
598 	CM = tgetstr("cm", &ap);
599 	LL = tgetstr("ll", &ap);
600 
601 	KL = tgetstr("kl", &ap);
602 	KR = tgetstr("kr", &ap);
603 	KU = tgetstr("ku", &ap);
604 	KD = tgetstr("kd", &ap);
605 	Klength = strlen(KL);
606 		/*	NOTE:   If KL, KR, KU, and KD are not
607 		 *		all the same length, some problems
608 		 *		may arise, since tests are made on
609 		 *		all of them together.
610 		 */
611 
612 	TI = tgetstr("ti", &ap);
613 	TE = tgetstr("te", &ap);
614 	KS = tgetstr("ks", &ap);
615 	KE = tgetstr("ke", &ap);
616 
617 	xPC = tgetstr("pc", &ap);
618 	if (xPC)
619 		PC = *xPC;
620 
621 	NDlength = strlen(ND);
622 	BSlength = strlen(BS);
623 	if ((CM == 0) &&
624 		(HO == 0 | UP==0 || BS==0 || ND==0)) {
625 		fprintf(stderr, "Terminal must have addressible ");
626 		fprintf(stderr, "cursor or home + 4 local motions\n");
627 		exit(5);
628 	}
629 	if (tgetflag("os")) {
630 		fprintf(stderr, "Terminal must not overstrike\n");
631 		exit(5);
632 	}
633 	if (LINES <= 0 || COLUMNS <= 0) {
634 		fprintf(stderr, "Must know the screen size\n");
635 		exit(5);
636 	}
637 
638 	gtty(0, &orig);
639 	new=orig;
640 	new.sg_flags &= ~(ECHO|CRMOD|ALLDELAY|XTABS);
641 	new.sg_flags |= CBREAK;
642 	signal(SIGINT,stop);
643 	ospeed = orig.sg_ospeed;
644 #ifdef TIOCGLTC
645 	ioctl(0, TIOCGLTC, &olttyc);
646 	nlttyc = olttyc;
647 	nlttyc.t_suspc = '\377';
648 	nlttyc.t_dsuspc = '\377';
649 #endif
650 	raw();
651 
652 	if ((orig.sg_flags & XTABS) == XTABS) TA=0;
653 	putpad(KS);
654 	putpad(TI);
655 	point(&cursor,0,LINES-1);
656 }
657