1 /* screen.c - Generic screen manipulation
2  *	Copyright (c) 1995-1997 Stefan Jokisch
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  */
20 
21 #include "frotz.h"
22 
23 extern void set_header_extension(int, zword);
24 
25 extern int direct_call(zword);
26 
27 static struct {
28 	enum story story_id;
29 	int pic;
30 	int pic1;
31 	int pic2;
32 } mapper[] = {
33 	{ ZORK_ZERO,  5, 497, 498},
34 	{ ZORK_ZERO,  6, 501, 502},
35 	{ ZORK_ZERO,  7, 499, 500},
36 	{ ZORK_ZERO,  8, 503, 504},
37 	{    ARTHUR, 54, 170, 171},
38 	{    SHOGUN, 50,  61,  62},
39 	{   UNKNOWN,  0,   0,   0}
40 };
41 
42 /* These are usually out of date.  Always update before using. */
43 static int font_height = 1;
44 static int font_width = 1;
45 
46 static bool input_redraw = FALSE;
47 static bool more_prompts = TRUE;
48 static bool discarding = FALSE;
49 static bool cursor = TRUE;
50 
51 static int input_window = 0;
52 
53 static Zwindow wp[8], *cwp = wp;
54 
curwinrec()55 Zwindow *curwinrec()
56 {
57 	return cwp;
58 }
59 
60 
61 /*
62  * winarg0
63  *
64  * Return the window number in zargs[0]. In V6 only, -3 refers to the
65  * current window.
66  *
67  */
winarg0(void)68 static zword winarg0(void)
69 {
70 	if (z_header.version == V6 && (short)zargs[0] == -3)
71 		return cwin;
72 
73 	if (zargs[0] >= ((z_header.version == V6) ? 8 : 2))
74 		runtime_error(ERR_ILL_WIN);
75 
76 	return zargs[0];
77 } /* winarg0 */
78 
79 
80 /*
81  * winarg2
82  *
83  * Return the (optional) window number in zargs[2]. -3 refers to the
84  * current window. This optional window number was only used by some
85  * V6 opcodes: set_cursor, set_margins, set_colour.
86  *
87  */
winarg2(void)88 static zword winarg2(void)
89 {
90 	if (zargc < 3 || (short)zargs[2] == -3)
91 		return cwin;
92 
93 	if (zargs[2] >= 8)
94 		runtime_error(ERR_ILL_WIN);
95 
96 	return zargs[2];
97 } /* winarg2 */
98 
99 
100 /*
101  * update_cursor
102  *
103  * Move the hardware cursor to make it match the window properties.
104  *
105  */
update_cursor(void)106 static void update_cursor(void)
107 {
108 	os_set_cursor(cwp->y_pos + cwp->y_cursor - 1,
109 		      cwp->x_pos + cwp->x_cursor - 1);
110 } /* update_cursor */
111 
112 
113 /*
114  * reset_cursor
115  *
116  * Reset the cursor of a given window to its initial position.
117  *
118  */
reset_cursor(zword win)119 static void reset_cursor(zword win)
120 {
121 	int lines = 0;
122 
123 	if (z_header.version <= V4 && win == 0)
124 		lines = wp[0].y_size / hi(wp[0].font_size) - 1;
125 
126 	wp[win].y_cursor = hi(wp[0].font_size) * lines + 1;
127 	wp[win].x_cursor = wp[win].left + 1;
128 
129 	if (win == cwin)
130 		update_cursor();
131 } /* reset_cursor */
132 
133 
134 /*
135  * reset_screen
136  *
137  * Do any interface-independent screen cleanup prior to exiting the game.
138  *
139  */
reset_screen(void)140 void reset_screen(void)
141 {
142 	if (need_newline_at_exit)
143 		new_line();
144 } /* reset_screen */
145 
146 
147 /*
148  * set_more_prompts
149  *
150  * Turn more prompts on/off.
151  *
152  */
set_more_prompts(bool flag)153 void set_more_prompts(bool flag)
154 {
155 	if (flag && !more_prompts)
156 		cwp->line_count = 0;
157 	more_prompts = flag;
158 } /* set_more_prompts */
159 
160 
161 /*
162  * units_left
163  *
164  * Return the #screen units from the cursor to the end of the line.
165  *
166  */
units_left(void)167 static int units_left(void)
168 {
169 	return cwp->x_size - cwp->right - cwp->x_cursor + 1;
170 } /* units_left */
171 
172 
173 /*
174  * get_max_width
175  *
176  * Return maximum width of a line in the given window. This is used in
177  * connection with the extended output stream #3 call in V6.
178  *
179  */
get_max_width(zword win)180 zword get_max_width(zword win)
181 {
182 	if (z_header.version == V6) {
183 		if (win >= 8)
184 			runtime_error(ERR_ILL_WIN);
185 		return wp[win].x_size - wp[win].left - wp[win].right;
186 	} else
187 		return 0xffff;
188 } /* get_max_width */
189 
190 
191 /*
192  * countdown
193  *
194  * Decrement the newline counter. Call the newline interrupt when the
195  * counter hits zero. This is a helper function for screen_new_line.
196  *
197  */
countdown(void)198 static void countdown(void)
199 {
200 	if (cwp->nl_countdown != 0) {
201 		if (--cwp->nl_countdown == 0)
202 			direct_call(cwp->nl_routine);
203 	}
204 } /* countdown */
205 
206 
207 /*
208  * screen_new_line
209  *
210  * Print a newline to the screen.
211  *
212  */
screen_new_line(void)213 void screen_new_line(void)
214 {
215 	if (discarding)
216 		return;
217 
218 	/* Handle newline interrupts at the start (for most cases) */
219 	if (z_header.interpreter_number != INTERP_MSDOS || story_id != ZORK_ZERO
220 	    || z_header.release != 393)
221 		countdown();
222 
223 	/* Check whether the last input line gets destroyed */
224 	if (input_window == cwin)
225 		input_redraw = TRUE;
226 
227 	/* If the cursor has not reached the bottom line, then move it to
228 	   the next line; otherwise scroll the window or reset the cursor
229 	   to the top left. */
230 	cwp->x_cursor = cwp->left + 1;
231 
232 	os_font_data(0, &font_height, &font_width);
233 	if (cwp->y_cursor + 2 * font_height - 1 > cwp->y_size)
234 		if (enable_scrolling) {
235 			zword y = cwp->y_pos;
236 			zword x = cwp->x_pos;
237 			os_scroll_area(y,
238 				       x,
239 				       y + cwp->y_size - 1,
240 				       x + cwp->x_size - 1, font_height);
241 		} else
242 			cwp->y_cursor = 1;
243 	else
244 		cwp->y_cursor += font_height;
245 
246 	update_cursor();
247 
248 	/* See if we need to print a more prompt (unless the game has set
249 	   the line counter to -999 in order to suppress more prompts). */
250 	if (enable_scrolling && (short)cwp->line_count != -999) {
251 		zword above = (cwp->y_cursor - 1) / font_height;
252 		zword below = (cwp->y_size - cwp->y_cursor + 1) / font_height;
253 		cwp->line_count++;
254 		if ((short)cwp->line_count >= (short)above + below - 1) {
255 			if (more_prompts)
256 				os_more_prompt();
257 			cwp->line_count = f_setup.context_lines;
258 		}
259 	}
260 
261 	/* Handle newline interrupts at the end for Zork Zero under DOS */
262 	if (z_header.interpreter_number == INTERP_MSDOS && story_id == ZORK_ZERO
263 	    && z_header.release == 393)
264 		countdown();
265 } /* screen_new_line */
266 
267 
268 /*
269  * screen_char
270  *
271  * Display a single character on the screen.
272  *
273  */
screen_char(zchar c)274 void screen_char(zchar c)
275 {
276 	int width;
277 
278 	if (discarding)
279 		return;
280 
281 	if (c == ZC_INDENT && cwp->x_cursor != cwp->left + 1)
282 		c = ' ';
283 
284 	if (units_left() < (width = os_char_width(c))) {
285 		if (!enable_wrapping) {
286 			cwp->x_cursor = cwp->x_size - cwp->right;
287 			return;
288 		}
289 		screen_new_line();
290 	}
291 	os_display_char(c);
292 	cwp->x_cursor += width;
293 
294 } /* screen_char */
295 
296 
297 /*
298  * screen_word
299  *
300  * Display a string of characters on the screen. If the word doesn't fit
301  * then use wrapping or clipping depending on the current setting of the
302  * enable_wrapping flag.
303  *
304  */
screen_word(const zchar * s)305 void screen_word(const zchar * s)
306 {
307 	int width;
308 
309 	if (discarding)
310 		return;
311 
312 	if (*s == ZC_INDENT && cwp->x_cursor != cwp->left + 1)
313 		screen_char(*s++);
314 
315 	if (units_left() < (width = os_string_width(s))) {
316 		if (!enable_wrapping) {
317 			zchar c;
318 			while ((c = *s++) != 0) {
319 				if (c == ZC_NEW_FONT || c == ZC_NEW_STYLE) {
320 					int arg = (int)*s++;
321 					if (c == ZC_NEW_FONT)
322 						os_set_font(arg);
323 					if (c == ZC_NEW_STYLE)
324 						os_set_text_style(arg);
325 				} else
326 					screen_char(c);
327 			}
328 			return;
329 		}
330 		if (*s == ' ' || *s == ZC_INDENT || *s == ZC_GAP)
331 			width = os_string_width(++s);
332 
333 #ifdef AMIGA
334 		if (cwin == 0)
335 			Justifiable();
336 #endif
337 
338 		screen_new_line();
339 	}
340 	os_display_string(s);
341 	cwp->x_cursor += width;
342 } /* screen_word */
343 
344 
345 /*
346  * screen_write_input
347  *
348  * Display an input line on the screen. This is required during playback.
349  *
350  */
screen_write_input(const zchar * buf,zchar key)351 void screen_write_input(const zchar * buf, zchar key)
352 {
353 	int width;
354 
355 	if (units_left() < (width = os_string_width(buf)))
356 		screen_new_line();
357 
358 	os_display_string(buf);
359 	cwp->x_cursor += width;
360 
361 	if (key == ZC_RETURN)
362 		screen_new_line();
363 } /* screen_write_input */
364 
365 
366 /*
367  * screen_erase_input
368  *
369  * Remove an input line that has already been printed from the screen
370  * as if it was deleted by the player. This could be necessary during
371  * playback.
372  *
373  */
screen_erase_input(const zchar * buf)374 void screen_erase_input(const zchar * buf)
375 {
376 	if (buf[0] != 0) {
377 		int width = os_string_width(buf);
378 
379 		zword y;
380 		zword x;
381 
382 		cwp->x_cursor -= width;
383 
384 		y = cwp->y_pos + cwp->y_cursor - 1;
385 		x = cwp->x_pos + cwp->x_cursor - 1;
386 
387 		os_font_data(0, &font_height, &font_width);
388 		os_erase_area(y, x, y + font_height - 1, x + width - 1, -1);
389 		os_set_cursor(y, x);
390 	}
391 } /* screen_erase_input */
392 
393 
394 /*
395  * console_read_input
396  *
397  * Read an input line from the keyboard and return the terminating key.
398  *
399  */
console_read_input(int max,zchar * buf,zword timeout,bool continued)400 zchar console_read_input(int max, zchar * buf, zword timeout, bool continued)
401 {
402 	zchar key;
403 	int i;
404 
405 	/* Make sure there is some space for input */
406 	if (cwin == 0 && units_left() + os_string_width(buf) < 10 * font_width)
407 		screen_new_line();
408 
409 	/* Make sure the input line is visible */
410 	if (continued && input_redraw)
411 		screen_write_input(buf, -1);
412 
413 	input_window = cwin;
414 	input_redraw = FALSE;
415 
416 	/* Get input line from IO interface */
417 	cwp->x_cursor -= os_string_width(buf);
418 	key = os_read_line(max, buf, timeout, units_left(), continued);
419 	cwp->x_cursor += os_string_width(buf);
420 
421 	if (key != ZC_TIME_OUT) {
422 		for (i = 0; i < 8; i++)
423 			wp[i].line_count = 0;
424 	}
425 
426 	/* Add a newline if the input was terminated normally */
427 	if (key == ZC_RETURN)
428 		screen_new_line();
429 
430 	return key;
431 } /* console_read_input */
432 
433 
434 /*
435  * console_read_key
436  *
437  * Read a single keystroke and return it.
438  *
439  */
console_read_key(zword timeout)440 zchar console_read_key(zword timeout)
441 {
442 	zchar key;
443 	int i;
444 
445 	key = os_read_key(timeout, cursor);
446 
447 	if (key != ZC_TIME_OUT)
448 		for (i = 0; i < 8; i++)
449 			wp[i].line_count = 0;
450 
451 	return key;
452 
453 } /* console_read_key */
454 
455 
456 /*
457  * update_attributes
458  *
459  * Set the three enable_*** variables to make them match the attributes
460  * of the current window.
461  *
462  */
update_attributes(void)463 static void update_attributes(void)
464 {
465 	zword attr = cwp->attribute;
466 
467 	enable_wrapping = attr & 1;
468 	enable_scrolling = attr & 2;
469 	enable_scripting = attr & 4;
470 	enable_buffering = attr & 8;
471 
472 	/* Some story files forget to select wrapping for printing hints */
473 
474 	if (story_id == ZORK_ZERO && z_header.release == 366)
475 		if (cwin == 0)
476 			enable_wrapping = TRUE;
477 	if (story_id == SHOGUN && z_header.release <= 295)
478 		if (cwin == 0)
479 			enable_wrapping = TRUE;
480 } /* update_attributes */
481 
482 
483 /*
484  * refresh_text_style
485  *
486  * Set the right text style. This can be necessary when the fixed font
487  * flag is changed, or when a new window is selected, or when the game
488  * uses the set_text_style opcode.
489  *
490  */
refresh_text_style(void)491 void refresh_text_style(void)
492 {
493 	zword style;
494 
495 	if (z_header.version != V6) {
496 		style = wp[0].style;
497 
498 		if (cwin != 0 || z_header.flags & FIXED_FONT_FLAG)
499 			style |= FIXED_WIDTH_STYLE;
500 	} else
501 		style = cwp->style;
502 
503 	if (!ostream_memory && ostream_screen && enable_buffering) {
504 		print_char(ZC_NEW_STYLE);
505 		print_char(style);
506 
507 	} else
508 		os_set_text_style(style);
509 } /* refresh_text_style */
510 
511 
512 /*
513  * set_window
514  *
515  * Set the current window. In V6 every window has its own set of window
516  * properties such as colours, text style, cursor position and size.
517  *
518  */
set_window(zword win)519 static void set_window(zword win)
520 {
521 	flush_buffer();
522 
523 	cwin = win;
524 	cwp = wp + win;
525 
526 	update_attributes();
527 
528 	if (z_header.version == V6) {
529 		os_set_colour(lo(cwp->colour), hi(cwp->colour));
530 
531 		if (os_font_data(cwp->font, &font_height, &font_width))
532 			os_set_font(cwp->font);
533 
534 		os_set_text_style(cwp->style);
535 	} else
536 		refresh_text_style();
537 
538 	if (z_header.version != V6 && win != 0) {
539 		wp[win].y_cursor = 1;
540 		wp[win].x_cursor = 1;
541 	}
542 
543 	update_cursor();
544 } /* set_window */
545 
546 
547 /*
548  * erase_window
549  *
550  * Erase a window to background colour.
551  *
552  */
erase_window(zword win)553 void erase_window(zword win)
554 {
555 	zword y = wp[win].y_pos;
556 	zword x = wp[win].x_pos;
557 
558 	if (z_header.version == V6 && win != cwin
559 	    && z_header.interpreter_number != INTERP_AMIGA)
560 		os_set_colour(lo(wp[win].colour), hi(wp[win].colour));
561 
562 	os_erase_area(y,
563 		      x, y + wp[win].y_size - 1, x + wp[win].x_size - 1, win);
564 
565 	if (z_header.version == V6 && win != cwin
566 	    && z_header.interpreter_number != INTERP_AMIGA)
567 		os_set_colour(lo(cwp->colour), hi(cwp->colour));
568 
569 	reset_cursor(win);
570 
571 	wp[win].line_count = 0;
572 } /* erase_window */
573 
574 
575 /*
576  * split_window
577  *
578  * Divide the screen into upper (1) and lower (0) windows. In V3 the upper
579  * window appears below the status line.
580  *
581  */
split_window(zword height)582 void split_window(zword height)
583 {
584 	zword stat_height = 0;
585 
586 	flush_buffer();
587 
588 	/* Calculate height of status line and upper window */
589 	if (z_header.version != V6)
590 		height *= hi(wp[1].font_size);
591 
592 	if (z_header.version <= V3)
593 		stat_height = hi(wp[7].font_size);
594 
595 	/* Cursor of upper window mustn't be swallowed by the lower window */
596 	wp[1].y_cursor += wp[1].y_pos - 1 - stat_height;
597 
598 	wp[1].y_pos = 1 + stat_height;
599 	wp[1].y_size = height;
600 
601 	if ((short)wp[1].y_cursor > (short)wp[1].y_size)
602 		reset_cursor(1);
603 
604 	/* Cursor of lower window mustn't be swallowed by the upper window */
605 	wp[0].y_cursor += wp[0].y_pos - 1 - stat_height - height;
606 
607 	wp[0].y_pos = 1 + stat_height + height;
608 	wp[0].y_size = z_header.screen_height - stat_height - height;
609 
610 	if ((short)wp[0].y_cursor < 1)
611 		reset_cursor(0);
612 
613 	/* Erase the upper window in V3 only */
614 	if (z_header.version == V3 && height != 0)
615 		erase_window(1);
616 
617 } /* split_window */
618 
619 
620 /*
621  * erase_screen
622  *
623  * Erase the entire screen to background colour.
624  *
625  */
erase_screen(zword win)626 static void erase_screen(zword win)
627 {
628 	int i;
629 
630 	os_erase_area(1, 1, z_header.screen_height, z_header.screen_width, -2);
631 
632 	if ((short)win == -1) {
633 		split_window(0);
634 		set_window(0);
635 		reset_cursor(0);
636 	}
637 
638 	for (i = 0; i < 8; i++)
639 		wp[i].line_count = 0;
640 
641 } /* erase_screen */
642 
643 
644 /*
645  * resize_screen
646  *
647  * Try to adapt the window properties to a new screen size.
648  *
649  */
resize_screen(void)650 void resize_screen(void)
651 {
652 	/* V6 games are asked to redraw.  Other versions have no means for that
653 	   so we do what we can. */
654 	if (z_header.version == V6)
655 		z_header.flags |= REFRESH_FLAG;
656 	else {
657 		int scroll, h;
658 
659 		wp[0].x_size = z_header.screen_width;
660 		if (wp[0].x_cursor > z_header.screen_width)
661 			wp[0].x_cursor = z_header.screen_width;
662 		wp[1].x_size = z_header.screen_width;
663 		if (wp[1].x_cursor > z_header.screen_width)
664 			wp[1].x_cursor = z_header.screen_width;
665 		wp[7].x_size = z_header.screen_width;
666 		if (wp[7].x_cursor > z_header.screen_width)
667 			wp[7].x_cursor = z_header.screen_width;
668 
669 		h = z_header.screen_height - wp[1].y_size - wp[7].y_size;
670 		if (h > 0) {
671 			wp[0].y_size = h;
672 			scroll = wp[0].y_cursor - wp[0].y_size;
673 		} else {
674 			/* Just make a one line window at the bottom of the screen. */
675 			/*XXX We should probably adjust the other windows.  But how? */
676 			wp[0].y_size = 1;
677 			scroll =
678 			    wp[0].y_pos + wp[0].y_cursor - z_header.screen_height - 1;
679 			wp[0].y_pos = z_header.screen_height;
680 		}
681 		if (scroll > 0) {
682 			wp[0].y_cursor = wp[0].y_size;
683 			os_repaint_window(0, wp[0].y_pos + scroll, wp[0].y_pos,
684 					  wp[0].x_pos, wp[0].y_size,
685 					  wp[0].x_size);
686 		}
687 	}
688 } /* resize_screen */
689 
690 
691 /*
692  * restart_screen
693  *
694  * Prepare the screen for a new game.
695  *
696  */
restart_screen(void)697 void restart_screen(void)
698 {
699 	/* Use default settings */
700 	os_set_colour(z_header.default_foreground, z_header.default_background);
701 
702 	if (os_font_data(TEXT_FONT, &font_height, &font_width))
703 		os_set_font(TEXT_FONT);
704 
705 	os_set_text_style(0);
706 
707 	cursor = TRUE;
708 
709 	/* Initialise window properties */
710 	mwin = 1;
711 	for (cwp = wp; cwp < wp + 8; cwp++) {
712 		cwp->y_pos = 1;
713 		cwp->x_pos = 1;
714 		cwp->y_size = 0;
715 		cwp->x_size = 0;
716 		cwp->y_cursor = 1;
717 		cwp->x_cursor = 1;
718 		cwp->left = 0;
719 		cwp->right = 0;
720 		cwp->nl_routine = 0;
721 		cwp->nl_countdown = 0;
722 		cwp->style = 0;
723 		cwp->colour =
724 		    (z_header.default_background << 8) | z_header.default_foreground;
725 		cwp->font = TEXT_FONT;
726 		cwp->font_size = (font_height << 8) | font_width;
727 		cwp->attribute = 8;
728 	}
729 
730 	/* Prepare lower/upper windows and status line */
731 	wp[0].attribute = 15;
732 
733 	wp[0].left = f_setup.left_margin;
734 	wp[0].right = f_setup.right_margin;
735 
736 	wp[0].x_size = z_header.screen_width;
737 	wp[1].x_size = z_header.screen_width;
738 
739 	if (z_header.version <= V3)
740 		wp[7].x_size = z_header.screen_width;
741 
742 	os_restart_game(RESTART_WPROP_SET);
743 
744 	/* Clear the screen, unsplit it and select window 0 */
745 	erase_screen((zword) (-1));
746 
747 } /* restart_screen */
748 
749 
750 /*
751  * validate_click
752  *
753  * Return false if the last mouse click occurred outside the current
754  * mouse window; otherwise write the mouse arrow coordinates to the
755  * memory of the header extension table and return true.
756  *
757  */
validate_click(void)758 bool validate_click(void)
759 {
760 	if (mwin >= 0) {
761 		if (mouse_y < wp[mwin].y_pos
762 		    || mouse_y >= wp[mwin].y_pos + wp[mwin].y_size)
763 			return FALSE;
764 		if (mouse_x < wp[mwin].x_pos
765 		    || mouse_x >= wp[mwin].x_pos + wp[mwin].x_size)
766 			return FALSE;
767 
768 		z_header.x_mouse_y = mouse_y - wp[mwin].y_pos + 1;
769 		z_header.x_mouse_x = mouse_x - wp[mwin].x_pos + 1;
770 	} else {
771 		if (mouse_y < 1 || mouse_y > z_header.screen_height)
772 			return FALSE;
773 		if (mouse_x < 1 || mouse_x > z_header.screen_width)
774 			return FALSE;
775 
776 		z_header.x_mouse_y = mouse_y;
777 		z_header.x_mouse_x = mouse_x;
778 
779 	}
780 
781 	if (z_header.version != V6) {
782 		z_header.x_mouse_y = (z_header.x_mouse_y - 1) / z_header.font_height + 1;
783 		z_header.x_mouse_x = (z_header.x_mouse_x - 1) / z_header.font_width + 1;
784 	}
785 
786 	set_header_extension(HX_MOUSE_Y, z_header.x_mouse_y);
787 	set_header_extension(HX_MOUSE_X, z_header.x_mouse_x);
788 
789 	return TRUE;
790 } /* validate_click */
791 
792 
793 /*
794  * screen_mssg_on
795  *
796  * Start printing a so-called debugging message. The contents of the
797  * message are passed to the message stream, a Frotz specific output
798  * stream with maximum priority.
799  *
800  */
screen_mssg_on(void)801 void screen_mssg_on(void)
802 {
803 	if (cwin == 0) {	/* messages in window 0 only */
804 		os_set_text_style(0);
805 
806 		if (cwp->x_cursor != cwp->left + 1)
807 			screen_new_line();
808 
809 		screen_char(ZC_INDENT);
810 	} else
811 		discarding = TRUE;	/* discard messages in other windows */
812 } /* screen_mssg_on */
813 
814 
815 /*
816  * screen_mssg_off
817  *
818  * Stop printing a "debugging" message.
819  *
820  */
screen_mssg_off(void)821 void screen_mssg_off(void)
822 {
823 	if (cwin == 0) {		/* messages in window 0 only */
824 		screen_new_line();
825 		refresh_text_style();
826 	} else
827 		discarding = FALSE;	/* message has been discarded */
828 
829 } /* screen_mssg_off */
830 
831 
832 /*
833  * z_buffer_mode, turn text buffering on/off.
834  *
835  *	zargs[0] = new text buffering flag (0 or 1)
836  *
837  */
z_buffer_mode(void)838 void z_buffer_mode(void)
839 {
840 	/* Infocom's V6 games rarely use the buffer_mode opcode. If they do
841 	   then only to print text immediately, without any delay. This was
842 	   used to give the player some sign of life while the game was
843 	   spending much time on parsing a complicated input line. (To turn
844 	   off word wrapping, V6 games use the window_style opcode instead.)
845 	   Today we can afford to ignore buffer_mode in V6. */
846 
847 	if (z_header.version != V6) {
848 		flush_buffer();
849 		wp[0].attribute &= ~8;
850 
851 		if (zargs[0] != 0)
852 			wp[0].attribute |= 8;
853 
854 		update_attributes();
855 	}
856 } /* z_buffer_mode */
857 
858 
859 /*
860  * z_draw_picture, draw a picture.
861  *
862  *	zargs[0] = number of picture to draw
863  *	zargs[1] = y-coordinate of top left corner
864  *	zargs[2] = x-coordinate of top left corner
865  *
866  */
z_draw_picture(void)867 void z_draw_picture(void)
868 {
869 	zword pic = zargs[0];
870 
871 	zword y = zargs[1];
872 	zword x = zargs[2];
873 
874 	int i;
875 
876 	flush_buffer();
877 
878 	if (y == 0)		/* use cursor line if y-coordinate is 0 */
879 		y = cwp->y_cursor;
880 	if (x == 0)		/* use cursor column if x-coordinate is 0 */
881 		x = cwp->x_cursor;
882 
883 	y += cwp->y_pos - 1;
884 	x += cwp->x_pos - 1;
885 
886 	/* The following is necessary to make Amiga and Macintosh story
887 	   files work with MCGA graphics files.  Some screen-filling
888 	   pictures of the original Amiga release like the borders of
889 	   Zork Zero were split into several MCGA pictures (left, right
890 	   and top borders).  We pretend this has not happened. */
891 
892 	for (i = 0; mapper[i].story_id != UNKNOWN; i++) {
893 		if (story_id == mapper[i].story_id && pic == mapper[i].pic) {
894 			int height1, width1;
895 			int height2, width2;
896 
897 			int delta = 0;
898 
899 			os_picture_data(pic, &height1, &width1);
900 			os_picture_data(mapper[i].pic2, &height2, &width2);
901 
902 			if (story_id == ARTHUR && pic == 54)
903 				delta = z_header.screen_width / 160;
904 
905 			os_draw_picture(mapper[i].pic1, y + height1, x + delta);
906 			os_draw_picture(mapper[i].pic2, y + height1,
907 					x + width1 - width2 - delta);
908 
909 		}
910 	}
911 
912 	os_draw_picture(pic, y, x);
913 	if (story_id == SHOGUN)
914 		if (pic == 3) {
915 			int height, width;
916 
917 			os_picture_data(59, &height, &width);
918 			os_draw_picture(59, y, z_header.screen_width - width + 1);
919 		}
920 } /* z_draw_picture */
921 
922 
923 /*
924  * z_erase_line, erase the line starting at the cursor position.
925  *
926  *	zargs[0] = 1 + #units to erase (1 clears to the end of the line)
927  *
928  */
z_erase_line(void)929 void z_erase_line(void)
930 {
931 	zword pixels = zargs[0];
932 	zword y, x;
933 
934 	flush_buffer();
935 
936 	/* Clipping at the right margin of the current window */
937 	if (--pixels == 0 || pixels > units_left())
938 		pixels = units_left();
939 
940 	/* Erase from cursor position */
941 	y = cwp->y_pos + cwp->y_cursor - 1;
942 	x = cwp->x_pos + cwp->x_cursor - 1;
943 
944 	os_font_data(0, &font_height, &font_width);
945 	os_erase_area(y, x, y + font_height - 1, x + pixels - 1, -1);
946 } /* z_erase_line */
947 
948 
949 /*
950  * z_erase_picture, erase a picture with background colour.
951  *
952  *	zargs[0] = number of picture to erase
953  *	zargs[1] = y-coordinate of top left corner (optional)
954  *	zargs[2] = x-coordinate of top left corner (optional)
955  *
956  */
z_erase_picture(void)957 void z_erase_picture(void)
958 {
959 	int height, width;
960 
961 	zword y = zargs[1];
962 	zword x = zargs[2];
963 
964 	flush_buffer();
965 
966 	if (y == 0)		/* use cursor line if y-coordinate is 0 */
967 		y = cwp->y_cursor;
968 	if (x == 0)		/* use cursor column if x-coordinate is 0 */
969 		x = cwp->x_cursor;
970 
971 	os_picture_data(zargs[0], &height, &width);
972 
973 	y += cwp->y_pos - 1;
974 	x += cwp->x_pos - 1;
975 
976 	os_erase_area(y, x, y + height - 1, x + width - 1, -1);
977 } /* z_erase_picture */
978 
979 
980 /*
981  * z_erase_window, erase a window or the screen to background colour.
982  *
983  *	zargs[0] = window (-3 current, -2 screen, -1 screen & unsplit)
984  *
985  */
z_erase_window(void)986 void z_erase_window(void)
987 {
988 	flush_buffer();
989 
990 	if ((short)zargs[0] == -1 || (short)zargs[0] == -2)
991 		erase_screen(zargs[0]);
992 	else
993 		erase_window(winarg0());
994 } /* z_erase_window */
995 
996 
997 /*
998  * z_get_cursor, write the cursor coordinates into a table.
999  *
1000  *	zargs[0] = address to write information to
1001  *
1002  */
z_get_cursor(void)1003 void z_get_cursor(void)
1004 {
1005 	zword y, x;
1006 
1007 	flush_buffer();
1008 
1009 	y = cwp->y_cursor;
1010 	x = cwp->x_cursor;
1011 
1012 	if (z_header.version != V6) {	/* convert to grid positions */
1013 		y = (y - 1) / z_header.font_height + 1;
1014 		x = (x - 1) / z_header.font_width + 1;
1015 	}
1016 
1017 	storew((zword) (zargs[0] + 0), y);
1018 	storew((zword) (zargs[0] + 2), x);
1019 } /* z_get_cursor */
1020 
1021 
1022 /*
1023  * z_get_wind_prop, store the value of a window property.
1024  *
1025  *	zargs[0] = window (-3 is the current one)
1026  *	zargs[1] = number of window property to be stored
1027  *
1028  */
z_get_wind_prop(void)1029 void z_get_wind_prop(void)
1030 {
1031 	flush_buffer();
1032 
1033 	if (zargs[1] >= 16)
1034 		runtime_error(ERR_ILL_WIN_PROP);
1035 
1036 	store(((zword *) (wp + winarg0()))[zargs[1]]);
1037 } /* z_get_wind_prop */
1038 
1039 
1040 /*
1041  * z_mouse_window, select a window as mouse window.
1042  *
1043  *	zargs[0] = window number (-3 is the current) or -1 for the screen
1044  *
1045  */
z_mouse_window(void)1046 void z_mouse_window(void)
1047 {
1048 	mwin = ((short)zargs[0] == -1) ? -1 : winarg0();
1049 } /* z_mouse_window */
1050 
1051 
1052 /*
1053  * z_move_window, place a window on the screen.
1054  *
1055  *	zargs[0] = window (-3 is the current one)
1056  *	zargs[1] = y-coordinate
1057  *	zargs[2] = x-coordinate
1058  *
1059  */
z_move_window(void)1060 void z_move_window(void)
1061 {
1062 	zword win = winarg0();
1063 
1064 	flush_buffer();
1065 
1066 	wp[win].y_pos = zargs[1];
1067 	wp[win].x_pos = zargs[2];
1068 
1069 	if (win == cwin)
1070 		update_cursor();
1071 } /* z_move_window */
1072 
1073 
1074 /*
1075  * z_picture_data, get information on a picture or the graphics file.
1076  *
1077  *	zargs[0] = number of picture or 0 for the graphics file
1078  *	zargs[1] = address to write information to
1079  *
1080  */
z_picture_data(void)1081 void z_picture_data(void)
1082 {
1083 	zword pic = zargs[0];
1084 	zword table = zargs[1];
1085 
1086 	int height, width;
1087 	int i;
1088 
1089 	bool avail = os_picture_data(pic, &height, &width);
1090 
1091 	for (i = 0; mapper[i].story_id != UNKNOWN; i++)
1092 		if (story_id == mapper[i].story_id) {
1093 			if (pic == mapper[i].pic) {
1094 				int height2, width2;
1095 
1096 				avail &=
1097 				    os_picture_data(mapper[i].pic1, &height2,
1098 						    &width2);
1099 				avail &=
1100 				    os_picture_data(mapper[i].pic2, &height2,
1101 						    &width2);
1102 
1103 				height += height2;
1104 
1105 			} else if (pic == mapper[i].pic1
1106 				   || pic == mapper[i].pic2)
1107 				avail = FALSE;
1108 		}
1109 
1110 	storew((zword) (table + 0), (zword) (height));
1111 	storew((zword) (table + 2), (zword) (width));
1112 
1113 	branch(avail);
1114 } /* z_picture_data */
1115 
1116 
1117 /*
1118  * z_picture_table, prepare a group of pictures for faster display.
1119  *
1120  *	zargs[0] = address of table holding the picture numbers
1121  *
1122  */
z_picture_table(void)1123 void z_picture_table(void)
1124 {
1125 	/* This opcode is used by Shogun and Zork Zero when the player
1126 	 * encounters built-in games such as Peggleboz. Nowadays it is
1127 	 * not very helpful to hold the picture data in memory because
1128 	 * even a small disk cache avoids re-loading of data.
1129 	 */
1130 } /* z_picture_table */
1131 
1132 
1133 /*
1134  * z_print_table, print ASCII text in a rectangular area.
1135  *
1136  *	zargs[0] = address of text to be printed
1137  *	zargs[1] = width of rectangular area
1138  *	zargs[2] = height of rectangular area (optional)
1139  *	zargs[3] = number of char's to skip between lines (optional)
1140  *
1141  */
z_print_table(void)1142 void z_print_table(void)
1143 {
1144 	zword addr = zargs[0];
1145 	zword x;
1146 	int i, j;
1147 
1148 	flush_buffer();
1149 
1150 	/* Supply default arguments */
1151 	if (zargc < 3)
1152 		zargs[2] = 1;
1153 	if (zargc < 4)
1154 		zargs[3] = 0;
1155 
1156 	/* Write text in width x height rectangle */
1157 	x = cwp->x_cursor;
1158 
1159 	for (i = 0; i < zargs[2]; i++) {
1160 		if (i != 0) {
1161 			if (z_header.version != V6 && cwin == 0) {
1162 				new_line();
1163 			} else {
1164 				flush_buffer();
1165 				os_font_data(0, &font_height, &font_width);
1166 				cwp->y_cursor += font_height;
1167 				cwp->x_cursor = x;
1168 				update_cursor();
1169 			}
1170 		}
1171 
1172 		for (j = 0; j < zargs[1]; j++) {
1173 			zbyte c;
1174 
1175 			LOW_BYTE(addr, c)
1176 			    addr++;
1177 			print_char(c);
1178 		}
1179 		addr += zargs[3];
1180 	}
1181 } /* z_print_table */
1182 
1183 
1184 /*
1185  * z_put_wind_prop, set the value of a window property.
1186  *
1187  *	zargs[0] = window (-3 is the current one)
1188  *	zargs[1] = number of window property to set
1189  *	zargs[2] = value to set window property to
1190  *
1191  */
z_put_wind_prop(void)1192 void z_put_wind_prop(void)
1193 {
1194 	flush_buffer();
1195 
1196 	if (zargs[1] >= 16)
1197 		runtime_error(ERR_ILL_WIN_PROP);
1198 
1199 	((zword *) (wp + winarg0()))[zargs[1]] = zargs[2];
1200 } /* z_put_wind_prop */
1201 
1202 
1203 /*
1204  * z_scroll_window, scroll a window up or down.
1205  *
1206  *	zargs[0] = window (-3 is the current one)
1207  *	zargs[1] = #screen units to scroll up (positive) or down (negative)
1208  *
1209  */
z_scroll_window(void)1210 void z_scroll_window(void)
1211 {
1212 	zword win = winarg0();
1213 	zword y, x;
1214 
1215 	flush_buffer();
1216 
1217 	/* Use the correct set of colours when scrolling the window */
1218 	if (win != cwin && z_header.interpreter_number != INTERP_AMIGA)
1219 		os_set_colour(lo(wp[win].colour), hi(wp[win].colour));
1220 
1221 	y = wp[win].y_pos;
1222 	x = wp[win].x_pos;
1223 
1224 	os_scroll_area(y,
1225 		       x,
1226 		       y + wp[win].y_size - 1,
1227 		       x + wp[win].x_size - 1, (short)zargs[1]);
1228 
1229 	if (win != cwin && z_header.interpreter_number != INTERP_AMIGA)
1230 		os_set_colour(lo(cwp->colour), hi(cwp->colour));
1231 } /* z_scroll_window */
1232 
1233 
1234 /*
1235  * z_set_colour, set the foreground and background colours.
1236  *
1237  *	zargs[0] = foreground colour
1238  *	zargs[1] = background colour
1239  *	zargs[2] = window (-3 is the current one, optional)
1240  *
1241  */
z_set_colour(void)1242 void z_set_colour(void)
1243 {
1244 	zword win = (z_header.version == V6) ? winarg2() : 0;
1245 
1246 	zword fg = zargs[0];
1247 	zword bg = zargs[1];
1248 
1249 	flush_buffer();
1250 
1251 	if ((short)fg == -1)	/* colour -1 is the colour at the cursor */
1252 		fg = os_peek_colour();
1253 	if ((short)bg == -1)
1254 		bg = os_peek_colour();
1255 
1256 	if (fg == 0)		/* colour 0 means keep current colour */
1257 		fg = lo(wp[win].colour);
1258 	if (bg == 0)
1259 		bg = hi(wp[win].colour);
1260 
1261 	if (fg == 1)		/* colour 1 is the system default colour */
1262 		fg = z_header.default_foreground;
1263 	if (bg == 1)
1264 		bg = z_header.default_background;
1265 
1266 	if (z_header.version == V6 && z_header.interpreter_number == INTERP_AMIGA)
1267 		/* Changing colours of window 0 affects the entire screen */
1268 		if (win == 0) {
1269 			int i;
1270 			for (i = 1; i < 8; i++) {
1271 				zword bg2 = hi(wp[i].colour);
1272 				zword fg2 = lo(wp[i].colour);
1273 
1274 				if (bg2 < 16)
1275 					bg2 =
1276 					    (bg2 == lo(wp[0].colour)) ? fg : bg;
1277 				if (fg2 < 16)
1278 					fg2 =
1279 					    (fg2 == lo(wp[0].colour)) ? fg : bg;
1280 
1281 				wp[i].colour = (bg2 << 8) | fg2;
1282 
1283 			}
1284 
1285 		}
1286 
1287 	wp[win].colour = (bg << 8) | fg;
1288 
1289 	if (win == cwin || z_header.version != V6)
1290 		os_set_colour(fg, bg);
1291 } /* z_set_colour */
1292 
1293 
1294 /*
1295  * z_set_font, set the font for text output and store the previous font.
1296  *
1297  * 	zargs[0] = number of font or 0 to keep current font
1298  *
1299  */
z_set_font(void)1300 void z_set_font(void)
1301 {
1302 	zword win = (z_header.version == V6) ? cwin : 0;
1303 	zword font = zargs[0];
1304 
1305 	if (font != 0) {
1306 		if (os_font_data(font, &font_height, &font_width)) {
1307 			store(wp[win].font);
1308 			wp[win].font = font;
1309 			wp[win].font_size = (font_height << 8) | font_width;
1310 
1311 			if (!ostream_memory && ostream_screen
1312 			    && enable_buffering) {
1313 				print_char(ZC_NEW_FONT);
1314 				print_char(font);
1315 
1316 			} else
1317 				os_set_font(font);
1318 		} else
1319 			store(0);
1320 	} else
1321 		store(wp[win].font);
1322 } /* z_set_font */
1323 
1324 
1325 /*
1326  * z_set_cursor, set the cursor position or turn the cursor on/off.
1327  *
1328  *	zargs[0] = y-coordinate or -2/-1 for cursor on/off
1329  *	zargs[1] = x-coordinate
1330  *	zargs[2] = window (-3 is the current one, optional)
1331  *
1332  */
z_set_cursor(void)1333 void z_set_cursor(void)
1334 {
1335 	zword win = (z_header.version == V6) ? winarg2() : 1;
1336 
1337 	zword y = zargs[0];
1338 	zword x = zargs[1];
1339 
1340 	flush_buffer();
1341 
1342 	/* Supply default arguments */
1343 	if (zargc < 3)
1344 		zargs[2] = -3;
1345 
1346 	/* Handle cursor on/off */
1347 	if ((short)y < 0) {
1348 		if ((short)y == -2)
1349 			cursor = TRUE;
1350 		if ((short)y == -1)
1351 			cursor = FALSE;
1352 		return;
1353 	}
1354 
1355 	/* Convert grid positions to screen units if this is not V6 */
1356 	if (z_header.version != V6) {
1357 		if (cwin == 0)
1358 			return;
1359 		y = (y - 1) * z_header.font_height + 1;
1360 		x = (x - 1) * z_header.font_width + 1;
1361 	}
1362 
1363 	/* Protect the margins */
1364 	if (y == 0)		/* use cursor line if y-coordinate is 0 */
1365 		y = wp[win].y_cursor;
1366 	if (x == 0)		/* use cursor column if x-coordinate is 0 */
1367 		x = wp[win].x_cursor;
1368 	if (x <= wp[win].left || x > wp[win].x_size - wp[win].right)
1369 		x = wp[win].left + 1;
1370 
1371 	/* Move the cursor */
1372 	wp[win].y_cursor = y;
1373 	wp[win].x_cursor = x;
1374 
1375 	if (win == cwin)
1376 		update_cursor();
1377 } /* z_set_cursor */
1378 
1379 
1380 /*
1381  * z_set_margins, set the left and right margins of a window.
1382  *
1383  *	zargs[0] = left margin in pixels
1384  *	zargs[1] = right margin in pixels
1385  *	zargs[2] = window (-3 is the current one, optional)
1386  *
1387  */
z_set_margins(void)1388 void z_set_margins(void)
1389 {
1390 	zword win = winarg2();
1391 
1392 	flush_buffer();
1393 
1394 	wp[win].left = zargs[0];
1395 	wp[win].right = zargs[1];
1396 
1397 	/* Protect the margins */
1398 	if (wp[win].x_cursor <= zargs[0]
1399 	    || wp[win].x_cursor > wp[win].x_size - zargs[1]) {
1400 
1401 		wp[win].x_cursor = zargs[0] + 1;
1402 
1403 		if (win == cwin)
1404 			update_cursor();
1405 	}
1406 } /* z_set_margins */
1407 
1408 
1409 /*
1410  * z_set_text_style, set the style for text output.
1411  *
1412  * 	zargs[0] = style flags to set or 0 to reset text style
1413  *
1414  */
z_set_text_style(void)1415 void z_set_text_style(void)
1416 {
1417 	zword win = (z_header.version == V6) ? cwin : 0;
1418 	zword style = zargs[0];
1419 
1420 	wp[win].style |= style;
1421 
1422 	if (style == 0)
1423 		wp[win].style = 0;
1424 
1425 	refresh_text_style();
1426 } /* z_set_text_style */
1427 
1428 
1429 /*
1430  * z_set_window, select the current window.
1431  *
1432  *	zargs[0] = window to be selected (-3 is the current one)
1433  *
1434  */
z_set_window(void)1435 void z_set_window(void)
1436 {
1437 	set_window(winarg0());
1438 } /* z_set_window */
1439 
1440 
1441 /*
1442  * pad_status_line
1443  *
1444  * Pad the status line with spaces up to the given position.
1445  *
1446  */
pad_status_line(int column)1447 static void pad_status_line(int column)
1448 {
1449 	int spaces;
1450 
1451 	flush_buffer();
1452 
1453 	spaces = units_left() / os_char_width(' ') - column;
1454 
1455 	/* while (spaces--) */
1456 	/* Justin Wesley's fix for narrow displays (Agenda PDA) */
1457 	while (spaces-- > 0)
1458 		screen_char(' ');
1459 } /* pad_status_line */
1460 
1461 
1462 /*
1463  * z_show_status, display the status line for V1 to V3 games.
1464  *
1465  *	no zargs used
1466  *
1467  */
z_show_status(void)1468 void z_show_status(void)
1469 {
1470 	zword global0;
1471 	zword global1;
1472 	zword global2;
1473 	zword addr;
1474 
1475 	bool brief = FALSE;
1476 
1477 	/* One V5 game (Wishbringer Solid Gold) contains this opcode by
1478 	   accident, so just return if the version number does not fit */
1479 	if (z_header.version >= V4)
1480 		return;
1481 
1482 	/* Read all relevant global variables from the memory of the
1483 	   Z-machine into local variables */
1484 	addr = z_header.globals;
1485 	LOW_WORD(addr, global0)
1486 	addr += 2;
1487 	LOW_WORD(addr, global1)
1488 	addr += 2;
1489 	LOW_WORD(addr, global2)
1490 	/* Frotz uses window 7 for the status line. Don't forget to select
1491 	   reverse and fixed width text style */
1492 	set_window(7);
1493 
1494 	print_char(ZC_NEW_STYLE);
1495 	print_char(REVERSE_STYLE | FIXED_WIDTH_STYLE);
1496 
1497 	/* If the screen width is below 55 characters then we have to use
1498 	   the brief status line format */
1499 	if (z_header.screen_cols < 55)
1500 		brief = TRUE;
1501 
1502 	/* Print the object description for the global variable 0 */
1503 	print_char(' ');
1504 	print_object(global0);
1505 
1506 	/* A header flag tells us whether we have to display the current
1507 	   time or the score/moves information */
1508 	if (z_header.config & CONFIG_TIME) {	/* print hours and minutes */
1509 		zword hours;
1510 
1511 		/* Cutthroats puts 111 in the hour when the PC is not
1512 		 * wearing a watch.  Most (all?) Infocom's interpreters
1513 		 * save for the Amiga one would handle 24-hour conversion
1514 		 * to am/pm notation by subtracting 12 from the hour if it's
1515 		 * greater than 12.  So, by putting 111 in there,
1516 		 * subtracting 12 results in 99.  The Amiga interpreter and
1517 		 * all modern interpreters do the conversion correctly.
1518 		 * The result is 3.
1519 		 */
1520 		if (story_id == CUTTHROATS && global1 >= 12)
1521 			hours = 99;
1522 		else
1523 			hours = (global1 + 11) % 12 + 1;
1524 
1525 		pad_status_line(brief ? 15 : 20);
1526 		print_string("Time: ");
1527 		if (hours < 10)
1528 			print_char(' ');
1529 		print_num(hours);
1530 		print_char(':');
1531 		if (global2 < 10)
1532 			print_char('0');
1533 		print_num(global2);
1534 		print_char(' ');
1535 		print_char((global1 >= 12) ? 'p' : 'a');
1536 		print_char('m');
1537 	} else {		/* print score and moves */
1538 		pad_status_line(brief ? 15 : 30);
1539 		print_string(brief ? "S: " : "Score: ");
1540 		print_num(global1);
1541 		pad_status_line(brief ? 8 : 14);
1542 		print_string(brief ? "M: " : "Moves: ");
1543 		print_num(global2);
1544 	}
1545 
1546 	/* Pad the end of the status line with spaces */
1547 	pad_status_line(0);
1548 
1549 	/* Return to the lower window */
1550 	set_window(0);
1551 } /* z_show_status */
1552 
1553 
1554 /*
1555  * z_split_window, split the screen into an upper (1) and lower (0) window.
1556  *
1557  *	zargs[0] = height of upper window in screen units (V6) or #lines
1558  *
1559  */
z_split_window(void)1560 void z_split_window(void)
1561 {
1562 	split_window(zargs[0]);
1563 } /* z_split_window */
1564 
1565 
1566 /*
1567  * z_window_size, change the width and height of a window.
1568  *
1569  *	zargs[0] = window (-3 is the current one)
1570  *	zargs[1] = new height in screen units
1571  *	zargs[2] = new width in screen units
1572  *
1573  */
z_window_size(void)1574 void z_window_size(void)
1575 {
1576 	zword win = winarg0();
1577 
1578 	flush_buffer();
1579 
1580 	wp[win].y_size = zargs[1];
1581 	wp[win].x_size = zargs[2];
1582 
1583 	/* Keep the cursor within the window */
1584 
1585 	if (wp[win].y_cursor > zargs[1] || wp[win].x_cursor > zargs[2])
1586 		reset_cursor(win);
1587 } /* z_window_size */
1588 
1589 
1590 /*
1591  * z_window_style, set / clear / toggle window attributes.
1592  *
1593  *	zargs[0] = window (-3 is the current one)
1594  *	zargs[1] = window attribute flags
1595  *	zargs[2] = operation to perform (optional, defaults to 0)
1596  *
1597  */
z_window_style(void)1598 void z_window_style(void)
1599 {
1600 	zword win = winarg0();
1601 	zword flags = zargs[1];
1602 
1603 	flush_buffer();
1604 
1605 	/* Supply default arguments */
1606 
1607 	if (zargc < 3)
1608 		zargs[2] = 0;
1609 
1610 	/* Set window style */
1611 
1612 	switch (zargs[2]) {
1613 	case 0:
1614 		wp[win].attribute = flags;
1615 		break;
1616 	case 1:
1617 		wp[win].attribute |= flags;
1618 		break;
1619 	case 2:
1620 		wp[win].attribute &= ~flags;
1621 		break;
1622 	case 3:
1623 		wp[win].attribute ^= flags;
1624 		break;
1625 	}
1626 
1627 	if (cwin == win)
1628 		update_attributes();
1629 } /* z_window_style */
1630 
1631 
1632 /*
1633  * get_window_colours
1634  *
1635  * Get the colours for a given window.
1636  *
1637  */
get_window_colours(zword win,zbyte * fore,zbyte * back)1638 void get_window_colours(zword win, zbyte * fore, zbyte * back)
1639 {
1640 	*fore = lo(wp[win].colour);
1641 	*back = hi(wp[win].colour);
1642 } /* get_window_colours */
1643 
1644 
1645 /*
1646  * get_window_font
1647  *
1648  * Get the font for a given window.
1649  *
1650  */
get_window_font(zword win)1651 zword get_window_font(zword win)
1652 {
1653 	zword font = wp[win].font;
1654 
1655 	if (font == TEXT_FONT) {
1656 		if (z_header.version != V6) {
1657 			if (win != 0 || z_header.flags & FIXED_FONT_FLAG)
1658 				font = FIXED_WIDTH_FONT;
1659 		} else {
1660 			if (wp[win].style & FIXED_WIDTH_STYLE)
1661 				font = FIXED_WIDTH_FONT;
1662 		}
1663 	}
1664 	return font;
1665 } /* get_window_font */
1666 
1667 
1668 /*
1669  * colour_in_use
1670  *
1671  * Check if a colour is set in any window.
1672  *
1673  */
colour_in_use(zword colour)1674 int colour_in_use(zword colour)
1675 {
1676 	int max = (z_header.version == V6) ? 8 : 2;
1677 	int i;
1678 
1679 	for (i = 0; i < max; i++) {
1680 		zword bg = hi(wp[i].colour);
1681 		zword fg = lo(wp[i].colour);
1682 
1683 		if (colour == fg || colour == bg)
1684 			return 1;
1685 	}
1686 	return 0;
1687 
1688 } /* colour_in_use */
1689 
1690 
1691 /*
1692  * get_current_window
1693  *
1694  * Get the currently active window.
1695  *
1696  */
get_current_window(void)1697 zword get_current_window(void)
1698 {
1699 	return cwp - wp;
1700 } /* get_current_window */
1701