xref: /original-bsd/games/snake/snake/move.c (revision 14054b48)
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.8 (Berkeley) 02/28/91";
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 #include <stdarg.h>
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 		pr("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)
138 			pr("ERROR: distance is negative: %d",distance);
139 		distance += abs(sp->line - cursor.line);
140 		if(distance >= CMlength){
141 			putpad(cmstr);
142 			cursor.line = sp->line;
143 			cursor.col = sp->col;
144 			return;
145 		}
146 	}
147 
148 	/*
149 	 * If we get here we have a terminal that can't cursor
150 	 * address but has local motions or one which can cursor
151 	 * address but can get there quicker with local motions.
152 	 */
153 	 gto(sp);
154 }
155 gto(sp)
156 struct point *sp;
157 {
158 
159 	int distance,f,tfield,j;
160 
161 	if (cursor.line > LINES || cursor.line <0 ||
162 	    cursor.col <0 || cursor.col > COLUMNS)
163 		pr("ERROR: cursor is at %d,%d\n",
164 			cursor.line,cursor.col);
165 	if (sp->line > LINES || sp->line <0 ||
166 	    sp->col <0 || sp->col >  COLUMNS)
167 		pr("ERROR: target is %d,%d\n",sp->line,sp->col);
168 	tfield = (sp->col) >> 3;
169 	if (sp->line == cursor.line){
170 		if (sp->col > cursor.col)right(sp);
171 		else{
172 			distance = (cursor.col -sp->col)*BSlength;
173 			if (((TA) &&
174 			     (distance > tfield+((sp->col)&7)*NDlength)
175 			    ) ||
176 			    (((cursor.col)*NDlength) < distance)
177 			   ){
178 				cr();
179 				right(sp);
180 			}
181 			else{
182 				while(cursor.col > sp->col) bs();
183 			}
184 		}
185 		return;
186 	}
187 				/*must change row */
188 	if (cursor.col - sp->col > (cursor.col >> 3)){
189 		if (cursor.col == 0)f = 0;
190 		else f = -1;
191 	}
192 	else f = cursor.col >> 3;
193 	if (((sp->line << 1) + 1 < cursor.line - f) && (HO != 0)){
194 			/*
195 			 * home quicker than rlf:
196 			 * (sp->line + f > cursor.line - sp->line)
197 			 */
198 		putpad(HO);
199 		cursor.col = cursor.line = 0;
200 		gto(sp);
201 		return;
202 	}
203 	if (((sp->line << 1) > cursor.line + LINES+1 + f) && (LL != 0)){
204 		/* home,rlf quicker than lf
205 		 * (LINES+1 - sp->line + f < sp->line - cursor.line)
206 		 */
207 		if (cursor.line > f + 1){
208 		/* is home faster than wraparound lf?
209 		 * (cursor.line + 20 - sp->line > 21 - sp->line + f)
210 		 */
211 			ll();
212 			gto(sp);
213 			return;
214 		}
215 	}
216 	if ((LL != 0) && (sp->line > cursor.line + (LINES >> 1) - 1))
217 		cursor.line += LINES;
218 	while(sp->line > cursor.line)down();
219 	while(sp->line < cursor.line)up();
220 	gto(sp);		/*can recurse since cursor.line = sp->line */
221 }
222 
223 right(sp)
224 struct point *sp;
225 {
226 	int field,tfield;
227 	int tabcol,strlength;
228 
229 	if (sp->col < cursor.col)
230 		pr("ERROR:right() can't move left\n");
231 	if(TA){		/* If No Tabs: can't send tabs because ttydrive
232 			 * loses count with control characters.
233 			 */
234 		field = cursor.col >> 3;
235 /*
236  *	This code is useful for a terminal which wraps around on backspaces.
237  *	(Mine does.)  Unfortunately, this is not specified in termcap, and
238  *	most terminals don't work that way.  (Of course, most terminals
239  *	have addressible cursors, too).
240  */
241 		if (BW && (CM == 0) &&
242 		    ((sp->col << 1) - field > (COLUMNS - 8) << 1 )
243 		   ){
244 	 		if (cursor.line == 0){
245 	 			outch('\n');
246 	 		}
247 	 		outch('\r');
248 	 		cursor.col = COLUMNS + 1;
249 	 		while(cursor.col > sp->col)bs();
250 	 		if (cursor.line != 0) outch('\n');
251 	 		return;
252 	 	}
253 
254 		tfield = sp->col >> 3;
255 
256 		while (field < tfield){
257 			putpad(TA);
258 			cursor.col = ++field << 3;
259 		}
260 		tabcol = (cursor.col|7) + 1;
261 		strlength = (tabcol - sp->col)*BSlength + 1;
262 		/* length of sequence to overshoot */
263 		if (((sp->col - cursor.col)*NDlength > strlength) &&
264 		    (tabcol < COLUMNS)
265 		   ){
266 			/*
267 			 * Tab past and backup
268 			 */
269 			putpad(TA);
270 			cursor.col = (cursor.col | 7) + 1;
271 			while(cursor.col > sp->col)bs();
272 		}
273 	}
274 	while (sp->col > cursor.col){
275 		nd();
276 	}
277 }
278 
279 cr(){
280 	outch('\r');
281 	cursor.col = 0;
282 }
283 
284 clear(){
285 	int i;
286 
287 	if (CL){
288 		putpad(CL);
289 		cursor.col=cursor.line=0;
290 	} else {
291 		for(i=0; i<LINES; i++) {
292 			putchar('\n');
293 		}
294 		cursor.line = LINES - 1;
295 		home();
296 	}
297 }
298 
299 home(){
300 	struct point z;
301 
302 	if(HO != 0){
303 		putpad(HO);
304 		cursor.col = cursor.line = 0;
305 		return;
306 	}
307 	z.col = z.line = 0;
308 	move(&z);
309 }
310 
311 ll(){
312 	int j,l;
313 	struct point z;
314 
315 	l = lcnt + 2;
316 	if(LL != NULL && LINES==l){
317 		putpad(LL);
318 		cursor.line = LINES-1;
319 		cursor.col = 0;
320 		return;
321 	}
322 	z.col = 0;
323 	z.line = l-1;
324 	move(&z);
325 }
326 
327 up(){
328 	putpad(UP);
329 	cursor.line--;
330 }
331 
332 down(){
333 	putpad(DO);
334 	cursor.line++;
335 	if (cursor.line >= LINES)cursor.line=LINES-1;
336 }
337 bs(){
338 	if (cursor.col > 0){
339 		putpad(BS);
340 		cursor.col--;
341 	}
342 }
343 
344 nd(){
345 	putpad(ND);
346 	cursor.col++;
347 	if (cursor.col == COLUMNS+1){
348 		cursor.line++;
349 		cursor.col = 0;
350 		if (cursor.line >= LINES)cursor.line=LINES-1;
351 	}
352 }
353 
354 pch(c)
355 {
356 	outch(c);
357 	if(++cursor.col >= COLUMNS && AM) {
358 		cursor.col = 0;
359 		++cursor.line;
360 	}
361 }
362 
363 apr(ps, fmt)
364 	struct point *ps;
365 	char *fmt;
366 {
367 	struct point p;
368 	va_list ap;
369 
370 	p.line = ps->line+1; p.col = ps->col+1;
371 	move(&p);
372 	va_start(ap, fmt);
373 	(void)vsprintf(str, fmt, ap);
374 	va_end(ap);
375 	pstring(str);
376 }
377 
378 pr(fmt)
379 	char *fmt;
380 {
381 	va_list ap;
382 
383 	va_start(ap, fmt);
384 	(void)vsprintf(str, fmt, ap);
385 	va_end(ap);
386 	pstring(str);
387 }
388 
389 pstring(s)
390 char *s;{
391 	struct point z;
392 	int stcol;
393 
394 	stcol = cursor.col;
395 	while (s[0] != '\0'){
396 		switch (s[0]){
397 		case '\n':
398 			move(point(&z,0,cursor.line+1));
399 			break;
400 		case '\r':
401 			move(point(&z,stcol,cursor.line+1));
402 			break;
403 		case '\t':
404 			z.col = (((cursor.col + 8) >> 3) << 3);
405 			z.line = cursor.line;
406 			move(&z);
407 			break;
408 		case '\b':
409 			bs();
410 			break;
411 		case CTRL('g'):
412 			outch(CTRL('g'));
413 			break;
414 		default:
415 			if (s[0] < ' ')break;
416 			pch(s[0]);
417 		}
418 		s++;
419 	}
420 }
421 
422 pchar(ps,ch)
423 struct point *ps;
424 char ch;{
425 	struct point p;
426 	p.col = ps->col + 1; p.line = ps->line + 1;
427 	if (
428 		(p.col >= 0) &&
429 		(p.line >= 0) &&
430 		(
431 			(
432 				(p.line < LINES) &&
433 				(p.col < COLUMNS)
434 			) ||
435 			(
436 	    			(p.col == COLUMNS) &&
437 				(p.line < LINES-1)
438 			)
439 	  	)
440 	){
441 		move(&p);
442 		pch(ch);
443 	}
444 }
445 
446 
447 outch(c)
448 {
449 	putchar(c);
450 }
451 
452 putpad(str)
453 char *str;
454 {
455 	if (str)
456 		tputs(str, 1, outch);
457 }
458 baudrate()
459 {
460 
461 	switch (orig.sg_ospeed){
462 	case B300:
463 		return(300);
464 	case B1200:
465 		return(1200);
466 	case B4800:
467 		return(4800);
468 	case B9600:
469 		return(9600);
470 	default:
471 		return(0);
472 	}
473 }
474 delay(t)
475 int t;
476 {
477 	int k,j;
478 
479 	k = baudrate() * t / 300;
480 	for(j=0;j<k;j++){
481 		putchar(PC);
482 	}
483 }
484 
485 done()
486 {
487 	cook();
488 	exit(0);
489 }
490 
491 cook()
492 {
493 	delay(1);
494 	putpad(TE);
495 	putpad(KE);
496 	fflush(stdout);
497 	stty(0, &orig);
498 #ifdef TIOCSLTC
499 	ioctl(0, TIOCSLTC, &olttyc);
500 #endif
501 }
502 
503 raw()
504 {
505 	stty(0, &new);
506 #ifdef TIOCSLTC
507 	ioctl(0, TIOCSLTC, &nlttyc);
508 #endif
509 }
510 
511 struct point *point(ps,x,y)
512 struct point *ps;
513 int x,y;
514 {
515 	ps->col=x;
516 	ps->line=y;
517 	return(ps);
518 }
519 
520 char *ap;
521 
522 getcap()
523 {
524 	char *getenv();
525 	char *term;
526 	char *xPC;
527 	struct point z;
528 	void stop();
529 
530 	term = getenv("TERM");
531 	if (term==0) {
532 		fprintf(stderr, "No TERM in environment\n");
533 		exit(1);
534 	}
535 
536 	switch (tgetent(tbuf, term)) {
537 	case -1:
538 		fprintf(stderr, "Cannot open termcap file\n");
539 		exit(2);
540 	case 0:
541 		fprintf(stderr, "%s: unknown terminal", term);
542 		exit(3);
543 	}
544 
545 	ap = tcapbuf;
546 
547 	LINES = tgetnum("li");
548 	COLUMNS = tgetnum("co");
549 	if (!lcnt)
550 		lcnt = LINES - 2;
551 	if (!ccnt)
552 		ccnt = COLUMNS - 3;
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|ALLDELAY|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 }
635