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