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