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