1 /*
2  * doutput.c - Dumb interface, output functions
3  *
4  * This file is part of Frotz.
5  *
6  * Frotz is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * Frotz is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  * Or visit http://www.fsf.org/
20  */
21 
22 #include "dfrotz.h"
23 
24 #define DEFAULT_DUMB_COLOUR 31
25 
26 extern f_setup_t f_setup;
27 
28 static bool show_line_numbers = FALSE;
29 static bool show_line_types = -1;
30 static bool show_pictures = TRUE;
31 static bool visual_bell = TRUE;
32 static bool plain_ascii = FALSE;
33 
34 static char latin1_to_ascii[] =
35 	"    !   c   L   >o< Y   |   S   ''  C   a   <<  not -   R   _   "
36 	"^0  +/- ^2  ^3  '   my  P   .   ,   ^1  o   >>  1/4 1/2 3/4 ?   "
37 	"A   A   A   A   Ae  A   AE  C   E   E   E   E   I   I   I   I   "
38 	"Th  N   O   O   O   O   Oe  *   O   U   U   U   Ue  Y   Th  ss  "
39 	"a   a   a   a   ae  a   ae  c   e   e   e   e   i   i   i   i   "
40 	"th  n   o   o   o   o   oe  :   o   u   u   u   ue  y   th  y   "
41 ;
42 
43 static char frotz_to_dumb [256];
44 
45 /* z_header.screen_rows * z_header.screen_cols */
46 static int screen_cells;
47 
48 /* The in-memory state of the screen.  */
49 /* Each cell contains a style in the lower byte and a zchar above. */
50 
51 typedef struct cell_struct {
52 	int style;
53 	short fg;
54 	short bg;
55 	zchar c;
56 } cell_t;
57 
58 static cell_t *screen_data;
59 
60 /* A cell's style is REVERSE_STYLE, normal (0), or PICTURE_STYLE.
61  * PICTURE_STYLE means the character is part of an ascii image outline
62  * box.  (This just buys us the ability to turn box display on and off
63  * with immediate effect.  No, not very useful, but I wanted to give
64  * the rv bit some company in that huge byte I allocated for it.)  */
65 
66 static int current_style = 0;
67 static char current_fg = DEFAULT_DUMB_COLOUR;
68 static char current_bg = DEFAULT_DUMB_COLOUR;
69 
70 /* Which cells have changed (1 byte per cell).  */
71 static char *screen_changes;
72 
73 static int cursor_row = 0, cursor_col = 0;
74 
75 /* Compression styles.  */
76 static enum {
77 	COMPRESSION_NONE,
78 	COMPRESSION_SPANS,
79 	COMPRESSION_MAX,
80 } compression_mode = COMPRESSION_SPANS;
81 static char *compression_names[] = {"NONE", "SPANS", "MAX"};
82 static int hide_lines = 0;
83 
84 /* Reverse-video display styles.  */
85 static enum {
86         RV_NONE,
87 	RV_DOUBLESTRIKE,
88 	RV_UNDERLINE,
89 	RV_CAPS,
90 } rv_mode = RV_NONE;
91 static char *rv_names[] = {"NONE", "DOUBLESTRIKE", "UNDERLINE", "CAPS"};
92 static char rv_blank_str[5] = {' ', 0, 0, 0, 0};
93 
94 
95 /*
96  * Local functions
97  */
98 #ifdef USE_UTF8
99 static void zputchar(zchar);
zputchar(zchar c)100 void zputchar(zchar c)
101 {
102 	if(c > 0x7ff) {
103 		putchar(0xe0 | ((c >> 12) & 0xf));
104 		putchar(0x80 | ((c >> 6) & 0x3f));
105 		putchar(0x80 | (c & 0x3f));
106 	} else if(c > 0x7f) {
107 		putchar(0xc0 | ((c >> 6) & 0x1f));
108 		putchar(0x80 | (c & 0x3f));
109 	} else {
110 		putchar(c);
111 	}
112 }
113 #else
114 #define zputchar(x) putchar(x)
115 #endif
116 
117 /* if val is '0' or '1', set *var accordingly, else toggle it.  */
toggle(bool * var,char val)118 static void toggle(bool *var, char val)
119 {
120 	*var = val == '1' || (val != '0' && !*var);
121 }
122 
123 
124 /* Print a cell to stdout using IRC formatting codes.  */
125 #ifndef DISABLE_FORMATS
show_cell_irc(cell_t cel)126 static void show_cell_irc(cell_t cel)
127 {
128 	static char lastfg   = DEFAULT_DUMB_COLOUR,
129 		    lastbg   = DEFAULT_DUMB_COLOUR,
130 		    lastbold = 0,
131 		    lastemph = 0;
132 
133 	char	    fg	     = cel.fg,
134 		    bg	     = cel.bg;
135 
136 	if (cel.style & REVERSE_STYLE) {
137 		fg = cel.bg;
138 		bg = cel.fg;
139 	}
140 
141 	if (fg != lastfg || bg != lastbg) {
142 		putchar ('\017');	/* ^O cancels all text styles */
143 		lastbold = 0;
144 		lastemph = 0;
145 
146 		if (fg != DEFAULT_DUMB_COLOUR) {
147 			if (bg != DEFAULT_DUMB_COLOUR)
148 				printf("\003%hhu,%s%hhu", fg, (bg < 10) ? "0" : "", bg);
149 			else
150 				printf("\003%s%hhu", (fg < 10) ? "0" : "", fg);
151 		}
152 	}
153 
154 	if (cel.style & BOLDFACE_STYLE) {
155 		if (!lastbold)
156 			putchar('\002');
157 		lastbold = 1;
158 	} else {
159 		if (lastbold)
160 			putchar('\002');
161 		lastbold = 0;
162 	}
163 
164 	if (cel.style & EMPHASIS_STYLE) {
165 		if (!lastemph)
166 			putchar('\037');
167 		lastemph = 1;
168 	} else {
169 		if (lastemph)
170 			putchar('\037');
171 		lastemph = 0;
172 	}
173 
174 	if (cel.style & PICTURE_STYLE)
175 		zputchar(show_pictures ? cel.c : ' ');
176 	else
177 		zputchar(cel.c);
178 
179 	lastfg = fg;
180 	lastbg = bg;
181 }
182 
show_cell_ansi(cell_t cel)183 static void show_cell_ansi(cell_t cel)
184 {
185 	static char lastfg   = DEFAULT_DUMB_COLOUR,
186 		    lastbg   = DEFAULT_DUMB_COLOUR,
187 		    lastbold = 0,
188 		    lastemph = 0,
189 		    lastrev  = 0;
190 
191 	char	    fg	     = cel.fg,
192 		    bg	     = cel.bg;
193 
194 	if (cel.c == '\n') {
195 		printf("\033[0K\n");
196 		return;
197 	}
198 
199 	if (fg == DEFAULT_DUMB_COLOUR)
200 		fg = frotz_to_dumb [z_header.default_foreground];
201 	if (bg == DEFAULT_DUMB_COLOUR)
202 		bg = frotz_to_dumb [z_header.default_background];
203 
204 	if (fg != lastfg) {
205 		if (fg < 8)
206 			printf("\033[%dm", 30 + fg);
207 		else
208 			printf("\033[38;5;%dm", 232 + fg);
209 		lastfg = fg;
210 	}
211 
212 	if (bg != lastbg) {
213 		if (bg < 8)
214 			printf("\033[%dm", 40 + bg);
215 		else
216 			printf("\033[48;5;%dm", 232 + bg);
217 		lastbg = bg;
218 	}
219 
220 	if (cel.style & REVERSE_STYLE) {
221 		if (!lastrev)
222 			printf("\033[7m");
223 		lastrev = 1;
224 	} else {
225 		if (lastrev)
226 			printf("\033[27m");
227 		lastrev = 0;
228 	}
229 
230 	if (cel.style & BOLDFACE_STYLE) {
231 		if (!lastbold)
232 			printf("\033[1m");
233 		lastbold = 1;
234 	} else {
235 		if (lastbold)
236 			printf("\033[22m");
237 		lastbold = 0;
238 	}
239 
240 	if (cel.style & EMPHASIS_STYLE) {
241 		if (!lastemph)
242 			printf("\033[4m");
243 		lastemph = 1;
244 	} else {
245 		if (lastemph)
246 			printf("\033[24m");
247 		lastemph = 0;
248 	}
249 
250 	if (cel.style & PICTURE_STYLE)
251 		zputchar(show_pictures ? cel.c : ' ');
252 	else
253 		zputchar(cel.c);
254 }
255 #endif /* DISABLE_FORMATS */
256 
257 
258 /* Print a cell to stdout without using formatting codes.  */
show_cell_normal(cell_t cel)259 static void show_cell_normal(cell_t cel)
260 {
261 	switch (cel.style) {
262 	case NORMAL_STYLE:
263 	case FIXED_WIDTH_STYLE:	/* NORMAL_STYLE falls through to here */
264 		zputchar(cel.c);
265 		break;
266 	case PICTURE_STYLE:
267 		zputchar(show_pictures ? cel.c : ' ');
268 		break;
269 	case REVERSE_STYLE:
270 		if (cel.c == ' ')
271 			printf("%s", rv_blank_str);
272 		else {
273 			switch (rv_mode) {
274 			case RV_CAPS:
275 				if (cel.c <= 0x7f) {
276 					zputchar(toupper(cel.c));
277 					break;
278 				}
279 			case RV_NONE:
280 				zputchar(cel.c);
281 				break;
282 			case RV_UNDERLINE:
283 				putchar('_');
284 				putchar('\b');
285 				zputchar(cel.c);
286 				break;
287 			case RV_DOUBLESTRIKE:
288 				zputchar(cel.c);
289 				putchar('\b');
290 				zputchar(cel.c);
291 				break;
292 			}
293 		}
294 		break;
295 	}
296 }
297 
298 
show_cell(cell_t cel)299 static void show_cell(cell_t cel)
300 {
301 #ifndef DISABLE_FORMATS
302 	if (f_setup.format == FORMAT_IRC)
303 		show_cell_irc(cel);
304 	else if (f_setup.format == FORMAT_ANSI)
305 		show_cell_ansi(cel);
306 	else
307 #endif
308 		show_cell_normal(cel);
309 }
310 
311 
will_print_blank(cell_t c)312 static bool will_print_blank(cell_t c)
313 {
314 #ifndef DISABLE_FORMATS
315 	if (f_setup.format != FORMAT_NORMAL)
316 		return FALSE;
317 #endif
318 	return (((c.style == PICTURE_STYLE) && !show_pictures)
319 		|| ((c.c == ' ')
320 		&& ((c.style != REVERSE_STYLE)
321 		|| (*rv_blank_str == ' '))));
322 }
323 
324 
make_cell(int style,short fg,short bg,zchar c)325 static cell_t make_cell(int style, short fg, short bg, zchar c)
326 {
327 	cell_t	cel;
328 
329 	cel.style = style;
330 	cel.c = c;
331 
332 	if (f_setup.format != FORMAT_NORMAL) {
333 		cel.bg = bg;
334 		cel.fg = fg;
335 	}
336 	return cel;
337 }
338 
339 
show_line_prefix(int row,char c)340 static void show_line_prefix(int row, char c)
341 {
342 	if (show_line_numbers) {
343 		if (row == -1) {
344 			show_cell(make_cell(0, DEFAULT_DUMB_COLOUR, DEFAULT_DUMB_COLOUR, '.'));
345 			show_cell(make_cell(0, DEFAULT_DUMB_COLOUR, DEFAULT_DUMB_COLOUR, '.'));
346 		}
347 		else {
348 			char s[4];
349 			sprintf(s, "%02d", (row + 1) % 100);
350 			show_cell(make_cell(0, DEFAULT_DUMB_COLOUR, DEFAULT_DUMB_COLOUR, s[0]));
351 			show_cell(make_cell(0, DEFAULT_DUMB_COLOUR, DEFAULT_DUMB_COLOUR, s[1]));
352 		}
353 	}
354 	if (show_line_types)
355 		show_cell(make_cell(0, DEFAULT_DUMB_COLOUR, DEFAULT_DUMB_COLOUR, c));
356 
357 	/* Add a separator char (unless there's nothing to separate).  */
358 	if (show_line_numbers || show_line_types)
359 		show_cell(make_cell(0, DEFAULT_DUMB_COLOUR, DEFAULT_DUMB_COLOUR, ' '));
360 }
361 
362 
dumb_row(int r)363 static cell_t *dumb_row(int r)
364 {
365 	return screen_data + r * z_header.screen_cols;
366 }
367 
368 
369 /* Print a row to stdout.  */
show_row(int r)370 static void show_row(int r)
371 {
372 	if (r == -1) {
373 		show_line_prefix(-1, '.');
374 	} else {
375 		int c, last;
376 		show_line_prefix(r, (r == cursor_row) ? ']' : ' ');
377 		/* Don't print spaces at end of line.  */
378 		/* (Saves bandwidth and printhead wear.)  */
379 		/* TODO: compress spaces to tabs.  */
380 		for (last = z_header.screen_cols - 1; last >= 0; last--) {
381 			if (!will_print_blank(dumb_row(r)[last]))
382 				break;
383 		}
384 
385 		for (c = 0; c <= last; c++)
386 			show_cell(dumb_row(r)[c]);
387 	}
388 	show_cell(make_cell (0, DEFAULT_DUMB_COLOUR, DEFAULT_DUMB_COLOUR, '\n'));
389 }
390 
391 
dumb_changes_row(int r)392 static char *dumb_changes_row(int r)
393 {
394 	return screen_changes + r * z_header.screen_cols;
395 }
396 
397 
398 /* Set a cell and update screen_changes.  */
dumb_set_cell(int row,int col,cell_t c)399 static void dumb_set_cell(int row, int col, cell_t c)
400 {
401 	cell_t test;
402 	bool result = FALSE;
403 
404 	test = dumb_row(row)[col];
405 
406 	if (c.style  == test.style &&
407 		c.fg == test.fg    &&
408 		c.bg == test.bg    &&
409 		c.c  == test.c) {
410 		result = TRUE;
411 	}
412 
413 	dumb_changes_row(row)[col] = (!result);
414 	dumb_row(row)[col] = c;
415 }
416 
417 
418 /* put a character in the cell at the cursor and advance the cursor.  */
dumb_display_char(zchar c)419 static void dumb_display_char(zchar c)
420 {
421 	dumb_set_cell(cursor_row, cursor_col, make_cell(current_style, current_fg, current_bg, c));
422 	if (++cursor_col == z_header.screen_cols) {
423 		if (cursor_row == z_header.screen_rows - 1)
424 			cursor_col--;
425 		else {
426 			cursor_row++;
427 			cursor_col = 0;
428 		}
429 	}
430 }
431 
432 
mark_all_unchanged(void)433 static void mark_all_unchanged(void)
434 {
435 	memset(screen_changes, 0, screen_cells);
436 }
437 
438 
439 /* Check if a cell is a blank or will display as one.
440  * (Used to help decide if contents are worth printing.)  */
is_blank(cell_t c)441 static bool is_blank(cell_t c)
442 {
443 	return ((c.c == ' ')
444 		|| ((c.style == PICTURE_STYLE) && !show_pictures));
445 }
446 
447 
dumb_copy_cell(int dest_row,int dest_col,int src_row,int src_col)448 static void dumb_copy_cell(int dest_row, int dest_col,
449 			int src_row, int src_col)
450 {
451 	dumb_row(dest_row)[dest_col] = dumb_row(src_row)[src_col];
452 	dumb_changes_row(dest_row)[dest_col] = dumb_changes_row(src_row)[src_col];
453 }
454 
455 
456 
457 /*
458  * Public functions, mostly for the core.
459  */
os_display_char(zchar c)460 void os_display_char (zchar c)
461 {
462 	if (c >= ZC_LATIN1_MIN) {
463 		if (plain_ascii) {
464 			char *ptr = latin1_to_ascii + 4 * (c - ZC_LATIN1_MIN);
465 			do
466 				dumb_display_char(*ptr++);
467 			while (*ptr != ' ');
468 		} else
469 			dumb_display_char(c);
470 	} else if (c >= 32 && c <= 126) {
471 		dumb_display_char(c);
472 	} else if (c == ZC_GAP) {
473 		dumb_display_char(' ');
474 		dumb_display_char(' ');
475 	} else if (c == ZC_INDENT) {
476 		dumb_display_char(' ');
477 		dumb_display_char(' ');
478 		dumb_display_char(' ');
479 	}
480 	return;
481 }
482 
483 
484 /* Haxor your boxor? */
os_display_string(const zchar * s)485 void os_display_string (const zchar *s)
486 {
487 	zchar c;
488 
489 	while ((c = *s++) != 0) {
490 		if (c == ZC_NEW_FONT)
491 			s++;
492 		else if (c == ZC_NEW_STYLE)
493 			os_set_text_style(*s++);
494 		else {
495 			os_display_char (c);
496 		}
497 	}
498 }
499 
500 
os_erase_area(int top,int left,int bottom,int right,int UNUSED (win))501 void os_erase_area (int top, int left, int bottom, int right, int UNUSED (win))
502 {
503 	int row, col;
504 	top--; left--; bottom--; right--;
505 	for (row = top; row <= bottom; row++) {
506 		for (col = left; col <= right; col++)
507 			dumb_set_cell(row, col, make_cell(current_style, current_fg, current_bg, ' '));
508 	}
509 }
510 
511 
os_scroll_area(int top,int left,int bottom,int right,int units)512 void os_scroll_area (int top, int left, int bottom, int right, int units)
513 {
514 	int row, col;
515 
516 	top--; left--; bottom--; right--;
517 
518 	if (units > 0) {
519 		for (row = top; row <= bottom - units; row++) {
520 			for (col = left; col <= right; col++)
521 				dumb_copy_cell(row, col, row + units, col);
522 		}
523 		os_erase_area(bottom - units + 2, left + 1,
524 			bottom + 1, right + 1, -1 );
525 	} else if (units < 0) {
526 		for (row = bottom; row >= top - units; row--) {
527 			for (col = left; col <= right; col++)
528 				dumb_copy_cell(row, col, row + units, col);
529 		}
530 		os_erase_area(top + 1, left + 1, top - units, right + 1 , -1);
531 	}
532 }
533 
534 
os_font_data(int font,int * height,int * width)535 int os_font_data(int font, int *height, int *width)
536 {
537 	if (font == TEXT_FONT) {
538 		*height = 1;
539 		*width = 1;
540 		return 1;
541 	}
542 	return 0;
543 }
544 
545 
os_set_colour(int newfg,int newbg)546 void os_set_colour (int newfg, int newbg)
547 {
548 	current_fg = frotz_to_dumb[newfg];
549 	current_bg = frotz_to_dumb[newbg];
550 }
551 
552 
os_reset_screen(void)553 void os_reset_screen(void)
554 {
555 	dumb_show_screen(FALSE);
556 }
557 
558 
os_beep(int volume)559 void os_beep (int volume)
560 {
561 	if (visual_bell)
562 		printf("[%s-PITCHED BEEP]\n", (volume == 1) ? "HIGH" : "LOW");
563 	else
564 		putchar('\a'); /* so much for dumb.  */
565 }
566 
567 
568 /* To make the common code happy */
os_set_font(int UNUSED (x))569 void os_set_font (int UNUSED (x)) {}
os_init_sound(void)570 void os_init_sound(void) {}
os_prepare_sample(int UNUSED (a))571 void os_prepare_sample (int UNUSED (a)) {}
os_finish_with_sample(int UNUSED (a))572 void os_finish_with_sample (int UNUSED (a)) {}
os_start_sample(int UNUSED (a),int UNUSED (b),int UNUSED (c),zword UNUSED (d))573 void os_start_sample (int UNUSED (a), int UNUSED (b), int UNUSED (c), zword UNUSED (d)) {}
os_stop_sample(int UNUSED (a))574 void os_stop_sample (int UNUSED (a)) {}
575 
os_check_unicode(int font,zchar c)576 int os_check_unicode(int font, zchar c)
577 {
578 	/* Only UTF-8 output, no input yet.  */
579 	return 1;
580 }
581 
582 
os_char_width(zchar z)583 int os_char_width (zchar z)
584 {
585 	if (plain_ascii && z >= ZC_LATIN1_MIN) {
586 		char *p = latin1_to_ascii + 4 * (z - ZC_LATIN1_MIN);
587 		return strchr(p, ' ') - p;
588 	}
589 	return 1;
590 }
591 
592 
os_string_width(const zchar * s)593 int os_string_width (const zchar *s)
594 {
595 	int width = 0;
596 	zchar c;
597 
598 	while ((c = *s++) != 0) {
599 		if (c == ZC_NEW_STYLE || c == ZC_NEW_FONT)
600 			s++;
601 		else
602 			width += os_char_width(c);
603 	}
604 	return width;
605 }
606 
607 
os_set_cursor(int row,int col)608 void os_set_cursor(int row, int col)
609 {
610 	cursor_row = row - 1; cursor_col = col - 1;
611 	if (cursor_row >= z_header.screen_rows)
612 		cursor_row = z_header.screen_rows - 1;
613 }
614 
615 
os_repaint_window(int UNUSED (win),int UNUSED (ypos_old),int UNUSED (ypos_new),int UNUSED (xpos),int UNUSED (ysize),int UNUSED (xsize))616 bool os_repaint_window(int UNUSED(win), int UNUSED(ypos_old),
617 			int UNUSED(ypos_new), int UNUSED(xpos),
618 			int UNUSED(ysize), int UNUSED(xsize))
619 {
620 	return FALSE;
621 }
622 
623 
os_set_text_style(int x)624 void os_set_text_style(int x)
625 {
626 	current_style = x;
627 }
628 
629 
630 /*
631  * Public functions just for the Dumb interface.
632  */
dumb_set_picture_cell(int row,int col,zchar c)633 void dumb_set_picture_cell(int row, int col, zchar c)
634 {
635 	dumb_set_cell(row, col, make_cell(current_style | PICTURE_STYLE, current_fg, current_bg, c));
636 }
637 
638 
dumb_init_output(void)639 void dumb_init_output(void)
640 {
641 #ifndef DISABLE_FORMATS
642 	if (f_setup.format == FORMAT_IRC) {
643 		setvbuf(stdout, 0, _IONBF, 0);
644 		setvbuf(stderr, 0, _IONBF, 0);
645 
646 		z_header.config |= CONFIG_COLOUR | CONFIG_BOLDFACE | CONFIG_EMPHASIS;
647 
648 		memset (frotz_to_dumb, 256, DEFAULT_DUMB_COLOUR);
649 		frotz_to_dumb [BLACK_COLOUR]   = 1;
650 		frotz_to_dumb [RED_COLOUR]     = 4;
651 		frotz_to_dumb [GREEN_COLOUR]   = 3;
652 		frotz_to_dumb [YELLOW_COLOUR]  = 8;
653 		frotz_to_dumb [BLUE_COLOUR]    = 12;
654 		frotz_to_dumb [MAGENTA_COLOUR] = 6;
655 		frotz_to_dumb [CYAN_COLOUR]    = 11;
656 		frotz_to_dumb [WHITE_COLOUR]   = 0;
657 		frotz_to_dumb [GREY_COLOUR]    = 14;
658 
659 		z_header.default_foreground = WHITE_COLOUR;
660 		z_header.default_background = BLACK_COLOUR;
661 	} else if (f_setup.format == FORMAT_ANSI) {
662 		setvbuf(stdout, 0, _IONBF, 0);
663 		setvbuf(stderr, 0, _IONBF, 0);
664 
665 		z_header.config |= CONFIG_COLOUR | CONFIG_BOLDFACE | CONFIG_EMPHASIS;
666 
667 		memset (frotz_to_dumb, 256, DEFAULT_DUMB_COLOUR);
668 		frotz_to_dumb [BLACK_COLOUR]      = 0;
669 		frotz_to_dumb [RED_COLOUR]        = 1;
670 		frotz_to_dumb [GREEN_COLOUR]      = 2;
671 		frotz_to_dumb [YELLOW_COLOUR]     = 3;
672 		frotz_to_dumb [BLUE_COLOUR]       = 4;
673 		frotz_to_dumb [MAGENTA_COLOUR]    = 5;
674 		frotz_to_dumb [CYAN_COLOUR]       = 6;
675 		frotz_to_dumb [WHITE_COLOUR]      = 7;
676 		frotz_to_dumb [LIGHTGREY_COLOUR]  = 17;
677 		frotz_to_dumb [MEDIUMGREY_COLOUR] = 13;
678 		frotz_to_dumb [DARKGREY_COLOUR]   = 8;
679 
680 		z_header.default_foreground = WHITE_COLOUR;
681 		z_header.default_background = BLACK_COLOUR;
682 	}
683 #endif /* DISABLE_FORMATS */
684 	if (z_header.version == V3) {
685 		z_header.config |= CONFIG_SPLITSCREEN;
686 		z_header.flags &= ~OLD_SOUND_FLAG;
687 	}
688 
689 	if (z_header.version >= V5) {
690 		z_header.flags &= ~SOUND_FLAG;
691 	}
692 
693 	z_header.screen_height = z_header.screen_rows;
694 	z_header.screen_width = z_header.screen_cols;
695 	screen_cells = z_header.screen_rows * z_header.screen_cols;
696 
697 	z_header.font_width = 1; z_header.font_height = 1;
698 
699 	if (show_line_types == -1)
700 		show_line_types = z_header.version > 3;
701 
702 	screen_data = malloc(screen_cells * sizeof(cell_t));
703 	screen_changes = malloc(screen_cells);
704 	os_erase_area(1, 1, z_header.screen_rows, z_header.screen_cols, -2);
705 	memset(screen_changes, 0, screen_cells);
706 }
707 
708 
dumb_display_user_input(char * s)709 void dumb_display_user_input(char *s)
710 {
711 	/* copy to screen without marking it as a change.  */
712 	while (*s)
713 		dumb_row(cursor_row)[cursor_col++] = make_cell(0, DEFAULT_DUMB_COLOUR, DEFAULT_DUMB_COLOUR, *s++);
714 }
715 
716 
dumb_discard_old_input(int num_chars)717 void dumb_discard_old_input(int num_chars)
718 {
719 	/* Weird discard stuff.  Grep spec for 'pain in my butt'.  */
720 	/* The old characters should be on the screen just before the cursor.
721 	 * Erase them.  */
722 	cursor_col -= num_chars;
723 
724 	if (cursor_col < 0)
725 		cursor_col = 0;
726 	os_erase_area(cursor_row + 1, cursor_col + 1,
727 	cursor_row + 1, cursor_col + num_chars, -1);
728 }
729 
730 
731 /* Print the part of the cursor row before the cursor.  */
dumb_show_prompt(bool show_cursor,char line_type)732 void dumb_show_prompt(bool show_cursor, char line_type)
733 {
734 	int i;
735 	show_line_prefix(show_cursor ? cursor_row : -1, line_type);
736 	if (show_cursor) {
737 		for (i = 0; i < cursor_col; i++)
738 			show_cell(dumb_row(cursor_row)[i]);
739 	}
740 }
741 
742 
743 /* Show the current screen contents, or what's changed since the last
744  * call.
745  *
746  * If compressing, and show_cursor is true, and the cursor is past the
747  * last nonblank character on the last line that would be shown, then
748  * don't show that line (because it will be redundant with the prompt
749  * line just below it).
750  */
dumb_show_screen(bool show_cursor)751 void dumb_show_screen(bool show_cursor)
752 {
753 	int r, c, first, last;
754 	char changed_rows[0x100];
755 
756 	/* Easy case */
757 	if (compression_mode == COMPRESSION_NONE) {
758 		for (r = hide_lines; r < z_header.screen_rows; r++)
759 			show_row(r);
760 		mark_all_unchanged();
761 		return;
762 	}
763 
764 	/* Check which rows changed, and where the first and last change is. */
765 	first = last = -1;
766 	memset(changed_rows, 0, z_header.screen_rows);
767 	for (r = hide_lines; r < z_header.screen_rows; r++) {
768 		for (c = 0; c < z_header.screen_cols; c++) {
769 			if (dumb_changes_row(r)[c] && !is_blank(dumb_row(r)[c]))
770 				break;
771 		}
772 
773 		changed_rows[r] = (c != z_header.screen_cols);
774 		if (changed_rows[r]) {
775 			first = (first != -1) ? first : r;
776 			last = r;
777 		}
778 	}
779 
780 	if (first == -1)
781 		return;
782 
783 	/* The show_cursor rule described above */
784 	if (show_cursor && (cursor_row == last)) {
785 		for (c = cursor_col; c < z_header.screen_cols; c++) {
786 			if (!is_blank(dumb_row(last)[c]))
787 				break;
788 		}
789 		if (c == z_header.screen_cols)
790 			last--;
791 	}
792 
793 	/* Display the appropriate rows.  */
794 	if (compression_mode == COMPRESSION_MAX) {
795 		for (r = first; r <= last; r++) {
796 			if (changed_rows[r])
797 				show_row(r);
798 		}
799 	} else {
800 		/* COMPRESSION_SPANS */
801 		for (r = first; r <= last; r++) {
802 			if (changed_rows[r] || changed_rows[r + 1])
803 				show_row(r);
804 			else {
805 				while (!changed_rows[r + 1])
806 					r++;
807 				show_row(-1);
808 			}
809 		}
810 		if (show_cursor && (cursor_row > last + 1))
811 			show_row((cursor_row == last + 2) ? (last + 1) : -1);
812 	}
813 	mark_all_unchanged();
814 }
815 
816 
817 /* Unconditionally show whole screen.  For \s user command.  */
dumb_dump_screen(void)818 void dumb_dump_screen(void)
819 {
820 	int r;
821 	for (r = 0; r < z_header.screen_height; r++)
822 		show_row(r);
823 }
824 
825 
826 /* Called when it's time for a more prompt but user has them turned off.  */
dumb_elide_more_prompt(void)827 void dumb_elide_more_prompt(void)
828 {
829     dumb_show_screen(FALSE);
830     if (compression_mode == COMPRESSION_SPANS && hide_lines == 0) {
831 	show_row(-1);
832     }
833 }
834 
835 
dumb_output_handle_setting(const char * setting,bool show_cursor,bool startup)836 bool dumb_output_handle_setting(const char *setting, bool show_cursor,
837 				bool startup)
838 {
839 	char *p;
840 	int i;
841 #ifdef USE_UTF8
842 	unsigned char *q;
843 #endif
844 
845 	if (!strncmp(setting, "pb", 2)) {
846 		toggle(&show_pictures, setting[2]);
847 		printf("Picture outlines display %s\n", show_pictures ? "ON" : "OFF");
848 		if (startup)
849 			return TRUE;
850 		for (i = 0; i < screen_cells; i++)
851 			screen_changes[i] = (screen_data[i].style == PICTURE_STYLE);
852 		dumb_show_screen(show_cursor);
853 	} else if (!strncmp(setting, "vb", 2)) {
854 		toggle(&visual_bell, setting[2]);
855 		printf("Visual bell %s\n", visual_bell ? "ON" : "OFF");
856 		os_beep(1); os_beep(2);
857 	} else if (!strncmp(setting, "ln", 2)) {
858 		toggle(&show_line_numbers, setting[2]);
859 		printf("Line numbering %s\n", show_line_numbers ? "ON" : "OFF");
860 	} else if (!strncmp(setting, "lt", 2)) {
861 		toggle(&show_line_types, setting[2]);
862 		printf("Line-type display %s\n", show_line_types ? "ON" : "OFF");
863 	} else if (*setting == 'c') {
864 		switch (setting[1]) {
865 			case 'm': compression_mode = COMPRESSION_MAX; break;
866 			case 's': compression_mode = COMPRESSION_SPANS; break;
867 			case 'n': compression_mode = COMPRESSION_NONE; break;
868 			case 'h': hide_lines = atoi(&setting[2]); break;
869 			default: return FALSE;
870 		}
871 		printf("Compression mode %s, hiding top %d lines\n",
872 		compression_names[compression_mode], hide_lines);
873 	} else if (*setting == 'r') {
874 		switch (setting[1]) {
875 		case 'n': rv_mode = RV_NONE; break;
876 		case 'o': rv_mode = RV_DOUBLESTRIKE; break;
877 		case 'u': rv_mode = RV_UNDERLINE; break;
878 		case 'c': rv_mode = RV_CAPS; break;
879 		case 'b': strncpy(rv_blank_str, setting[2] ? &setting[2] : " ", 4); break;
880 		default: return FALSE;
881 		}
882 #ifdef USE_UTF8
883 		for (q = (unsigned char *)&rv_blank_str[1]; *q; q++)
884 			if (*q < 0x80 || *q >= 0xc0 || (unsigned char)rv_blank_str[0] < 0x80)
885 				*q = 0;
886 #else
887 		rv_blank_str[1] = 0;
888 #endif
889 		printf("Reverse-video mode %s, blanks reverse to '%s': ",
890 			rv_names[rv_mode], rv_blank_str);
891 
892 		for (p = "sample reverse text"; *p; p++)
893 			show_cell(make_cell(REVERSE_STYLE, DEFAULT_DUMB_COLOUR, DEFAULT_DUMB_COLOUR, *p));
894 		putchar('\n');
895 		for (i = 0; i < screen_cells; i++)
896 			screen_changes[i] = (screen_data[i].style == REVERSE_STYLE);
897 		dumb_show_screen(show_cursor);
898 	} else if (!strcmp(setting, "set")) {
899 		printf("Compression Mode %s, hiding top %d lines\n",
900 			compression_names[compression_mode], hide_lines);
901 		printf("Picture Boxes display %s\n", show_pictures ? "ON" : "OFF");
902 		printf("Visual Bell %s\n", visual_bell ? "ON" : "OFF");
903 		os_beep(1); os_beep(2);
904 		printf("Line Numbering %s\n", show_line_numbers ? "ON" : "OFF");
905 		printf("Line-Type display %s\n", show_line_types ? "ON" : "OFF");
906 		printf("Reverse-Video mode %s, Blanks reverse to '%s': ",
907 			rv_names[rv_mode], rv_blank_str);
908 		for (p = "sample reverse text"; *p; p++)
909 			show_cell(make_cell(REVERSE_STYLE, DEFAULT_DUMB_COLOUR, DEFAULT_DUMB_COLOUR, *p));
910 		putchar('\n');
911 	} else
912 		return FALSE;
913 	return TRUE;
914 }
915 
916 
917