xref: /386bsd/usr/src/games/rogue/curses.c (revision a2142627)
1 /*
2  * Copyright (c) 1988 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Timothy C. Stoehr.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by the University of
19  *	California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 #ifndef lint
38 static char sccsid[] = "@(#)curses.c	5.3 (Berkeley) 6/1/90";
39 #endif /* not lint */
40 
41 /*
42  * curses.c
43  *
44  * This source herein may be modified and/or distributed by anybody who
45  * so desires, with the following restrictions:
46  *    1.)  No portion of this notice shall be removed.
47  *    2.)  Credit shall not be taken for the creation of this source.
48  *    3.)  This code is not to be traded, sold, or used for personal
49  *         gain or profit.
50  *
51  */
52 
53 #ifdef CURSES
54 
55 /* The following is a curses emulation package suitable for the rogue program
56  * in which it is included.  No other suitability is claimed or suspected.
57  * Only those routines currently needed by this rogue program are included.
58  * This is being provided for those systems that don't have a suitable
59  * curses package and want to run this rogue program.
60  *
61  * Compile the entire program with -DCURSES to incorporate this package.
62  *
63  * The following is NOT supported:
64  *   "%D", "%B", "%n", or "%>" inside a cursor motion (cm) termcap string.
65  *   Terminals in which the cursor motion addresses the row differently from
66  *       the column, as in ":cm=\E%2,%3" or ":cm=\EY%+x;%+y"
67  *   Termcap database stored in the TERMCAP environ variable as returned
68  *       from md_getenv().  Only the termcap file name can be stored there.
69  *       See the comments for md_getenv() in machdep.c.
70  *   Terminals without non-destructive backspace.  Backspace (^H) is used
71  *       for cursor motion regardless of any termcap entries.
72  *   The ":tc=" termcap entry is ignored.
73  *
74  * Suggestions:
75  *   Use line-feed as your termcap "do" entry: ":do=^J", ":do=\012" or
76  *      ":do=\n"  This will help cursor motion optimization.  If line-feed
77  *      won't work, then a short escape sequence will do.
78  */
79 
80 #include <stdio.h>
81 #include "rogue.h"
82 
83 boolean tc_tname();
84 
85 #define BS 010
86 #define LF 012
87 #define CR 015
88 #define ESC '\033'
89 #define TAB '\011'
90 
91 #define ST_MASK 0x80
92 #define BUFLEN 256
93 
94 char terminal[DROWS][DCOLS];
95 char buffer[DROWS][DCOLS];
96 char *tc_file;
97 
98 char cm_esc[16];
99 char cm_sep[16];
100 char cm_end[16];
101 boolean cm_reverse = 0;
102 boolean cm_two = 0;
103 boolean cm_three = 0;
104 boolean cm_char = 0;
105 short cm_inc = 0;
106 
107 boolean screen_dirty;
108 boolean lines_dirty[DROWS];
109 boolean buf_stand_out = 0;
110 boolean term_stand_out = 0;
111 
112 int LINES = DROWS;
113 int COLS = DCOLS;
114 WINDOW scr_buf;
115 WINDOW *curscr = &scr_buf;
116 
117 char *CL = (char *) 0;
118 char *CM = (char *) 0;
119 char *UC = (char *) 0;	/* UP */
120 char *DO = (char *) 0;
121 char *VS = "";
122 char *VE = "";
123 char *TI = "";
124 char *TE = "";
125 char *SO = "";
126 char *SE = "";
127 
128 short cur_row;
129 short cur_col;
130 
initscr()131 initscr()
132 {
133 	clear();
134 	get_term_info();
135 	printf("%s%s", TI, VS);
136 }
137 
endwin()138 endwin()
139 {
140 	printf("%s%s", TE, VE);
141 	md_cbreak_no_echo_nonl(0);
142 }
143 
move(row,col)144 move(row, col)
145 short row, col;
146 {
147 	curscr->_cury = row;
148 	curscr->_curx = col;
149 	screen_dirty = 1;
150 }
151 
mvaddstr(row,col,str)152 mvaddstr(row, col, str)
153 short row, col;
154 char *str;
155 {
156 	move(row, col);
157 	addstr(str);
158 }
159 
addstr(str)160 addstr(str)
161 char *str;
162 {
163 	while (*str) {
164 		addch((int) *str++);
165 	}
166 }
167 
addch(ch)168 addch(ch)
169 register int ch;
170 {
171 	short row, col;
172 
173 	row = curscr->_cury;
174 	col = curscr->_curx++;
175 
176 	if (buf_stand_out) {
177 		ch |= ST_MASK;
178 	}
179 	buffer[row][col] = (char) ch;
180 	lines_dirty[row] = 1;
181 	screen_dirty = 1;
182 }
183 
mvaddch(row,col,ch)184 mvaddch(row, col, ch)
185 short row, col;
186 int ch;
187 {
188 	move(row, col);
189 	addch(ch);
190 }
191 
refresh()192 refresh()
193 {
194 	register i, j, line;
195 	short old_row, old_col, first_row;
196 
197 	if (screen_dirty) {
198 
199 		old_row = curscr->_cury;
200 		old_col = curscr->_curx;
201 		first_row = cur_row;
202 
203 		for (i = 0; i < DROWS; i++) {
204 			line = (first_row + i) % DROWS;
205 			if (lines_dirty[line]) {
206 				for (j = 0; j < DCOLS; j++) {
207 					if (buffer[line][j] != terminal[line][j]) {
208 						put_char_at(line, j, buffer[line][j]);
209 					}
210 				}
211 				lines_dirty[line] = 0;
212 			}
213 		}
214 		put_cursor(old_row, old_col);
215 		screen_dirty = 0;
216 		fflush(stdout);
217 	}
218 }
219 
wrefresh(scr)220 wrefresh(scr)
221 WINDOW *scr;
222 {
223 	short i, col;
224 
225 	printf("%s", CL);
226 	cur_row = cur_col = 0;
227 
228 	for (i = 0; i < DROWS; i++) {
229 		col = 0;
230 		while (col < DCOLS) {
231 			while ((col < DCOLS) && (buffer[i][col] == ' ')) {
232 				col++;
233 			}
234 			if (col < DCOLS) {
235 				put_cursor(i, col);
236 			}
237 			while ((col < DCOLS) && (buffer[i][col] != ' ')) {
238 				put_st_char((int) buffer[i][col]);
239 				cur_col++;
240 				col++;
241 			}
242 		}
243 	}
244 	put_cursor(curscr->_cury, curscr->_curx);
245 	fflush(stdout);
246 	scr = scr;		/* make lint happy */
247 }
248 
mvinch(row,col)249 mvinch(row, col)
250 short row, col;
251 {
252 	move(row, col);
253 	return((int) buffer[row][col]);
254 }
255 
clear()256 clear()
257 {
258 	printf("%s", CL);
259 	fflush(stdout);
260 	cur_row = cur_col = 0;
261 	move(0, 0);
262 	clear_buffers();
263 }
264 
clrtoeol()265 clrtoeol()
266 {
267 	short row, col;
268 
269 	row = curscr->_cury;
270 
271 	for (col = curscr->_curx; col < DCOLS; col++) {
272 		buffer[row][col] = ' ';
273 	}
274 	lines_dirty[row] = 1;
275 }
276 
standout()277 standout()
278 {
279 	buf_stand_out = 1;
280 }
281 
standend()282 standend()
283 {
284 	buf_stand_out = 0;
285 }
286 
crmode()287 crmode()
288 {
289 	md_cbreak_no_echo_nonl(1);
290 }
291 
noecho()292 noecho()
293 {
294 	/* crmode() takes care of this */
295 }
296 
nonl()297 nonl()
298 {
299 	/* crmode() takes care of this */
300 }
301 
clear_buffers()302 clear_buffers()
303 {
304 	register i, j;
305 
306 	screen_dirty = 0;
307 
308 	for (i = 0; i < DROWS; i++) {
309 		lines_dirty[i] = 0;
310 		for (j = 0; j < DCOLS; j++) {
311 			terminal[i][j] = ' ';
312 			buffer[i][j] = ' ';
313 		}
314 	}
315 }
316 
put_char_at(row,col,ch)317 put_char_at(row, col, ch)
318 register row, col, ch;
319 {
320 	put_cursor(row, col);
321 	put_st_char(ch);
322 	terminal[row][col] = (char) ch;
323 	cur_col++;
324 }
325 
put_cursor(row,col)326 put_cursor(row, col)
327 register row, col;
328 {
329 	register i, rdif, cdif;
330 	short ch, t;
331 
332 	rdif = (row > cur_row) ? row - cur_row : cur_row - row;
333 	cdif = (col > cur_col) ? col - cur_col : cur_col - col;
334 
335 	if (((row > cur_row) && DO) || ((cur_row > row) && UC)) {
336 		if ((rdif < 4) && (cdif < 4)) {
337 			for (i = 0; i < rdif; i++) {
338 				printf("%s", ((row < cur_row) ? UC : DO));
339 			}
340 			cur_row = row;
341 			if (col == cur_col) {
342 				return;
343 			}
344 		}
345 	}
346 	if (row == cur_row) {
347 		if (cdif <= 6) {
348 		for (i = 0; i < cdif; i++) {
349 				ch = (col < cur_col) ? BS :
350 						terminal[row][cur_col + i];
351 				put_st_char((int) ch);
352 			}
353 			cur_row = row;
354 			cur_col = col;
355 			return;
356 		}
357 	}
358 	cur_row = row;
359 	cur_col = col;
360 
361 	row += cm_inc;
362 	col += cm_inc;
363 
364 	if (cm_reverse) {
365 		t = row;
366 		row = col;
367 		col = t;
368 	}
369 	if (cm_two) {
370 		printf("%s%02d%s%02d%s", cm_esc, row, cm_sep, col, cm_end);
371 	} else if (cm_three) {
372 		printf("%s%03d%s%03d%s", cm_esc, row, cm_sep, col, cm_end);
373 	} else if (cm_char) {
374 		printf("%s%c%s%c%s", cm_esc, row, cm_sep, col, cm_end);
375 	} else {
376 		printf("%s%d%s%d%s", cm_esc, row, cm_sep, col, cm_end);
377 	}
378 }
379 
put_st_char(ch)380 put_st_char(ch)
381 register ch;
382 {
383 	if ((ch & ST_MASK) && (!term_stand_out)) {
384 		ch &= ~ST_MASK;
385 		printf("%s%c", SO, ch);
386 		term_stand_out = 1;
387 	} else if ((!(ch & ST_MASK)) && term_stand_out) {
388 		printf("%s%c", SE, ch);
389 		term_stand_out = 0;
390 	} else {
391 		ch &= ~ST_MASK;
392 		putchar(ch);
393 	}
394 }
395 
get_term_info()396 get_term_info()
397 {
398 	FILE *fp;
399 	char *term, *tcf;
400 	char buf[BUFLEN];
401 
402 	if (tcf = md_getenv("TERMCAP")) {
403 		if (strlen(tcf) > 40) {
404 			clean_up("TERMCAP file name too long");
405 		}
406 		tc_file = tcf;
407 	} else {
408 		if (!(tc_file = md_gdtcf())) {
409 			clean_up("I need a termcap file");
410 		}
411 	}
412 
413 	if (!(term = md_getenv("TERM"))) {
414 		clean_up("Cannot find TERM variable in environ");
415 	}
416 	if ((fp = fopen(tc_file, "r")) == NULL) {
417 		sprintf(buf, "Cannot open TERMCAP file: %s", tc_file);
418 		clean_up(buf);
419 	}
420 
421 	if (!tc_tname(fp, term, buf)) {
422 		sprintf(buf, "Cannot find TERM type: %s in TERMCAP file: %s", term,
423 			tc_file);
424 		clean_up(buf);
425 	}
426 	tc_gtdata(fp, buf);
427 	fclose(fp);
428 }
429 
430 boolean
tc_tname(fp,term,buf)431 tc_tname(fp, term, buf)
432 FILE *fp;
433 char *term;
434 char *buf;
435 {
436 	short i, j;
437 	boolean found = 0;
438 	char *fg;
439 
440 	while (!found) {
441 		i = 0;
442 		fg = fgets(buf, BUFLEN, fp);
443 		if (fg != NULL) {
444 			if (	(buf[0] != '#') && (buf[0] != ' ') && (buf[0] != TAB) &&
445 					(buf[0] != CR) && (buf[0] != LF)) {
446 				while (buf[i] && (!found)) {
447 					j = 0;
448 					while (buf[i] == term[j]) {
449 						i++;
450 						j++;
451 					}
452 					if ((!term[j]) && ((buf[i] == '|') || (buf[i] == ':'))) {
453 						found = 1;
454 					} else {
455 						while (buf[i] && (buf[i] != '|') && (buf[i] != ':')) {
456 							i++;
457 						}
458 						if (buf[i]) {
459 							i++;
460 						}
461 					}
462 				}
463 			}
464 		} else {
465 			break;
466 		}
467 	}
468 	return(found);
469 }
470 
tc_gtdata(fp,buf)471 tc_gtdata(fp, buf)
472 FILE *fp;
473 char *buf;
474 {
475 	short i;
476 	boolean first = 1;
477 
478 	do {
479 		if (!first) {
480 			if ((buf[0] != TAB) && (buf[0] != ' ')) {
481 				break;
482 			}
483 		}
484 		first = 0;
485 		i = 0;
486 		while (buf[i]) {
487 			while (buf[i] && (buf[i] != ':')) {
488 				i++;
489 			}
490 			if (buf[i] == ':') {
491 				if (!strncmp(buf + i, ":cl=", 4)) {
492 					tc_gets(buf + i, &CL);
493 				} else if (!strncmp(buf + i, ":cm=", 4)) {
494 					tc_gets(buf + i, &CM);
495 				} else if (!strncmp(buf + i, ":up=", 4)) {
496 					tc_gets(buf + i, &UC);
497 				} else if (!strncmp(buf + i, ":do=", 4)) {
498 					tc_gets(buf + i, &DO);
499 				} else if (!strncmp(buf + i, ":vs=", 4)) {
500 					tc_gets(buf + i, &VS);
501 				} else if (!strncmp(buf + i, ":ve=", 4)) {
502 					tc_gets(buf + i, &VE);
503 				} else if (!strncmp(buf + i, ":ti=", 4)) {
504 					tc_gets(buf + i, &TI);
505 				} else if (!strncmp(buf + i, ":te=", 4)) {
506 					tc_gets(buf + i, &TE);
507 				} else if (!strncmp(buf + i, ":vs=", 4)) {
508 					tc_gets(buf + i, &VS);
509 				} else if (!strncmp(buf + i, ":ve=", 4)) {
510 					tc_gets(buf + i, &VE);
511 				} else if (!strncmp(buf + i, ":so=", 4)) {
512 					tc_gets(buf + i, &SO);
513 				} else if (!strncmp(buf + i, ":se=", 4)) {
514 					tc_gets(buf + i, &SE);
515 				} else if (!strncmp(buf + i, ":li#", 4)) {
516 					tc_gnum(buf + i, &LINES);
517 				} else if (!strncmp(buf + i, ":co#", 4)) {
518 					tc_gnum(buf + i, &COLS);
519 				}
520 				i++;
521 			}
522 		}
523 	} while (fgets(buf, BUFLEN, fp) != NULL);
524 
525 	if ((!CM) || (!CL)) {
526 		clean_up("Terminal and termcap must have cm and cl");
527 	}
528 	tc_cmget();
529 }
530 
tc_gets(ibuf,tcstr)531 tc_gets(ibuf, tcstr)
532 char *ibuf;
533 char **tcstr;
534 {
535 	short i, j, k, n;
536 	char obuf[BUFLEN];
537 
538 	i = 4;
539 	j = 0;
540 
541 	while (ibuf[i] && is_digit(ibuf[i])) {
542 		i++;
543 	}
544 
545 	while (ibuf[i] && (ibuf[i] != ':')) {
546 		if (ibuf[i] == '\\') {
547 			i++;
548 			switch(ibuf[i]) {
549 			case 'E':
550 				obuf[j] = ESC;
551 				i++;
552 				break;
553 			case 'n':
554 				obuf[j] = LF;
555 				i++;
556 				break;
557 			case 'r':
558 				obuf[j] = CR;
559 				i++;
560 				break;
561 			case 'b':
562 				obuf[j] = BS;
563 				i++;
564 				break;
565 			case 't':
566 				obuf[j] = TAB;
567 				i++;
568 				break;
569 			case '0':
570 			case '1':
571 			case '2':
572 			case '3':
573 			case '4':
574 			case '5':
575 			case '6':
576 			case '7':
577 			case '8':
578 			case '9':
579 				n = 0;
580 				k = 0;
581 				while (k < 3 && ibuf[i] && is_digit(ibuf[i])) {
582 					n = (8 * n) + (ibuf[i] - '0');
583 					i++;
584 					k++;
585 				}
586 				obuf[j] = (char) n;
587 				break;
588 			default:
589 				obuf[j] = ibuf[i];
590 				i++;
591 			}
592 		} else if (ibuf[i] == '^') {
593 			obuf[j] = ibuf[i+1] - 64;
594 			i += 2;
595 		} else {
596 			obuf[j] = ibuf[i++];
597 		}
598 		j++;
599 	}
600 	obuf[j] = 0;
601 	if (!(*tcstr = md_malloc(j + 1))) {
602 		clean_up("cannot alloc() memory");
603 	}
604 	(void) strcpy(*tcstr, obuf);
605 }
606 
tc_gnum(ibuf,n)607 tc_gnum(ibuf, n)
608 char *ibuf;
609 int *n;
610 {
611 	short i;
612 	int r = 0;
613 
614 	i = 4;
615 
616 	while (is_digit(ibuf[i])) {
617 		r = (r * 10) + (ibuf[i] - '0');
618 		i++;
619 	}
620 	*n = r;
621 }
622 
tstp()623 tstp()
624 {
625 	endwin();
626 	md_tstp();
627 
628 	start_window();
629 	printf("%s%s", TI, VS);
630 	wrefresh(curscr);
631 	md_slurp();
632 }
633 
tc_cmget()634 tc_cmget()
635 {
636 	short i = 0, j = 0, rc_spec = 0;
637 
638 	while (CM[i] && (CM[i] != '%') && (j < 15)) {
639 		cm_esc[j++] = CM[i++];
640 	}
641 	cm_esc[j] = 0;
642 
643 	while (CM[i] && (rc_spec < 2)) {
644 		if (CM[i] == '%') {
645 			i++;
646 			switch(CM[i]) {
647 			case 'd':
648 				rc_spec++;
649 				break;
650 			case 'i':
651 				cm_inc = 1;
652 				break;
653 			case '2':
654 				cm_two = 1;
655 				rc_spec++;
656 				break;
657 			case '3':
658 				cm_three = 1;
659 				rc_spec++;
660 				break;
661 			case '.':
662 				cm_char = 1;
663 				rc_spec++;
664 				break;
665 			case 'r':
666 				cm_reverse = 1;
667 				break;
668 			case '+':
669 				i++;
670 				cm_inc = CM[i];
671 				cm_char = 1;
672 				rc_spec++;
673 				break;
674 			}
675 			i++;
676 		} else {
677 			j = 0;
678 			while (CM[i] && (CM[i] != '%')) {
679 				cm_sep[j++] = CM[i++];
680 			}
681 			cm_sep[j] = 0;
682 		}
683 	}
684 
685 	j = 0;
686 	if (rc_spec == 2) {
687 		while (CM[i] && (j < 15)) {
688 			cm_end[j++] = CM[i++];
689 		}
690 	}
691 	cm_end[j] = 0;
692 }
693 
694 #endif
695