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