1 /* File: z-term.c */
2 
3 /*
4  * Copyright (c) 1997 Ben Harrison
5  *
6  * This software may be copied and distributed for educational, research,
7  * and not for profit purposes provided that this copyright and statement
8  * are included in all such copies.
9  */
10 
11 /* Purpose: a generic, efficient, terminal window package -BEN- */
12 #include "z-virt.h"
13 #include "z-term.h"
14 
15 /*
16  * This file provides a generic, efficient, terminal window package,
17  * which can be used not only on standard terminal environments such
18  * as dumb terminals connected to a Unix box, but also in more modern
19  * "graphic" environments, such as the Macintosh or Unix/X11.
20  *
21  * Each "window" works like a standard "dumb terminal", that is, it
22  * can display a two dimensional array of grids containing colored
23  * textual symbols, plus an optional cursor, and it can be used to
24  * get keypress events from the user.
25  *
26  * In fact, this package can simply be used, if desired, to support
27  * programs which will look the same on a dumb terminal as they do
28  * on a graphic platform such as the Macintosh.
29  *
30  * This package was designed to help port the game "Angband" to a wide
31  * variety of different platforms.  Angband, like many other games in
32  * the "rogue-like" heirarchy, requires, at the minimum, the ability
33  * to display "colored textual symbols" in a standard 80x24 "window",
34  * such as that provided by most dumb terminals, and many old personal
35  * computers, and to check for "keypresses" from the user.  The major
36  * concerns were thus portability and efficiency, so Angband could be
37  * easily ported to many different systems, with minimal effort, and
38  * yet would run quickly on each of these systems, no matter what kind
39  * of underlying hardware/software support was being used.
40  *
41  * It is important to understand the differences between the older
42  * "dumb terminals" and the newer "graphic interface" machines, since
43  * this package was designed to work with both types of systems.
44  *
45  * New machines:
46  *   waiting for a keypress is complex
47  *   checking for a keypress is often cheap
48  *   changing "colors" may be expensive
49  *   the "color" of a "blank" is rarely important
50  *   moving the "cursor" is relatively cheap
51  *   use a "software" cursor (only moves when requested)
52  *   drawing characters normally will not erase old ones
53  *   drawing a character on the cursor often erases it
54  *   may have fast routines for "clear a region"
55  *   the bottom right corner is usually not special
56  *
57  * Old machines:
58  *   waiting for a keypress is simple
59  *   checking for a keypress is often expensive
60  *   changing "colors" is usually cheap
61  *   the "color" of a "blank" may be important
62  *   moving the "cursor" may be expensive
63  *   use a "hardware" cursor (moves during screen updates)
64  *   drawing new symbols automatically erases old ones
65  *   characters may only be drawn at the cursor location
66  *   drawing a character on the cursor will move the cursor
67  *   may have fast routines for "clear entire window"
68  *   may have fast routines for "clear to end of line"
69  *   the bottom right corner is often dangerous
70  *
71  *
72  * This package provides support for multiple windows, each of an
73  * arbitrary size (up to 255x255), each with its own set of flags,
74  * and its own hooks to handle several low-level procedures which
75  * differ from platform to platform.  Then the main program simply
76  * creates one or more "term" structures, setting the various flags
77  * and hooks in a manner appropriate for the current platform, and
78  * then it can use the various "term" structures without worrying
79  * about the underlying platform.
80  *
81  *
82  * This package allows each "grid" in each window to hold an attr/char
83  * pair, with each ranging from 0 to 255, and makes very few assumptions
84  * about the meaning of any attr/char values.  Normally, we assume that
85  * "attr 0" is "black", with the semantics that "black" text should be
86  * sent to "Term_wipe()" instead of "Term_text()", but this sematics is
87  * modified if either the "always_pict" or the "always_text" flags are
88  * set.  We assume that "char 0" is "dangerous", since placing such a
89  * "char" in the middle of a string "terminates" the string, and usually
90  * we prevent its use.
91  *
92  * Finally, we use a special attr/char pair, defaulting to "attr 0" and
93  * "char 32", also known as "black space", when we "erase" or "clear"
94  * any window, but this pair can be redefined to any pair, including
95  * the standard "white space", or the bizarre "emptiness" ("attr 0"
96  * and "char 0"), as long as various obscure restrictions are met.
97  *
98  *
99  * This package provides several functions which allow a program to
100  * interact with the "term" structures.  Most of the functions allow
101  * the program to "request" certain changes to the current "term",
102  * such as moving the cursor, drawing an attr/char pair, erasing a
103  * region of grids, hiding the cursor, etc.  Then there is a special
104  * function which causes all of the "pending" requests to be performed
105  * in an efficient manner.  There is another set of functions which
106  * allow the program to query the "requested state" of the current
107  * "term", such as asking for the cursor location, or what attr/char
108  * is at a given location, etc.  There is another set of functions
109  * dealing with "keypress" events, which allows the program to ask if
110  * the user has pressed any keys, or to forget any keys the user pressed.
111  * There is a pair of functions to allow this package to memorize the
112  * contents of the current "term", and to restore these contents at
113  * a later time.  There is a special function which allows the program
114  * to specify which "term" structure should be the "current" one.  At
115  * the lowest level, there is a set of functions which allow a new
116  * "term" to be initialized or destroyed, and which allow this package,
117  * or a program, to access the special "hooks" defined for the current
118  * "term", and a set of functions which those "hooks" can use to inform
119  * this package of the results of certain occurances, for example, one
120  * such function allows this package to learn about user keypresses,
121  * detected by one of the special "hooks".
122  *
123  * We provide, among other things, the functions "Term_keypress()"
124  * to "react" to keypress events, and "Term_redraw()" to redraw the
125  * entire window, plus "Term_resize()" to note a new size.
126  *
127  *
128  * Note that the current "term" contains two "window images".  One of
129  * these images represents the "requested" contents of the "term", and
130  * the other represents the "actual" contents of the "term", at the time
131  * of the last performance of pending requests.  This package uses these
132  * two images to determine the "minimal" amount of work needed to make
133  * the "actual" contents of the "term" match the "requested" contents of
134  * the "term".  This method is not perfect, but it often reduces the
135  * amount of work needed to perform the pending requests, which thus
136  * increases the speed of the program itself.  This package promises
137  * that the requested changes will appear to occur either "all at once"
138  * or in a "top to bottom" order.  In addition, a "cursor" is maintained,
139  * and this cursor is updated along with the actual window contents.
140  *
141  * Currently, the "Term_fresh()" routine attempts to perform the "minimum"
142  * number of physical updates, in terms of total "work" done by the hooks
143  * Term_wipe(), Term_text(), and Term_pict(), making use of the fact that
144  * adjacent characters of the same color can both be drawn together using
145  * the "Term_text()" hook, and that "black" text can often be sent to the
146  * "Term_wipe()" hook instead of the "Term_text()" hook, and if something
147  * is already displayed in a window, then it is not necessary to display
148  * it again.  Unfortunately, this may induce slightly non-optimal results
149  * in some cases, in particular, those in which, say, a string of ten
150  * characters needs to be written, but the fifth character has already
151  * been displayed.  Currently, this will cause the "Term_text()" routine
152  * to be called once for each half of the string, instead of once for the
153  * whole string, which, on some machines, may be non-optimal behavior.
154  *
155  * The new formalism includes a "displayed" screen image (old) which
156  * is actually seen by the user, a "requested" screen image (scr)
157  * which is being prepared for display, and a list of screen images
158  * which are accessed through the "next" pointers from scr.
159  *
160  *
161  * Several "flags" are available in each "term" to allow the underlying
162  * visual system (which initializes the "term" structure) to "optimize"
163  * the performance of this package for the given system, or to request
164  * certain behavior which is helpful/required for the given system.
165  *
166  * The "soft_cursor" flag indicates the use of a "soft" cursor, which
167  * only moves when explicitly requested,and which is "erased" when
168  * any characters are drawn on top of it.  This flag is used for all
169  * "graphic" systems which handle the cursor by "drawing" it.
170  *
171  * The "icky_corner" flag indicates that the bottom right "corner"
172  * of the windows are "icky", and "printing" anything there may
173  * induce "messy" behavior, such as "scrolling".  This flag is used
174  * for most old "dumb terminal" systems.
175  *
176  *
177  * The "term" structure contains the following function "hooks":
178  *
179  *   Term->init_hook = Init the term
180  *   Term->nuke_hook = Nuke the term
181  *   Term->user_hook = Perform user actions
182  *   Term->xtra_hook = Perform extra actions
183  *   Term->curs_hook = Draw (or Move) the cursor
184  *   Term->wipe_hook = Draw some blank spaces
185  *   Term->text_hook = Draw some text in the window
186  *   Term->pict_hook = Draw some attr/chars in the window
187  *
188  * The "Term->user_hook" hook provides a simple hook to an implementation
189  * defined function, with application defined semantics.  It is available
190  * to the program via the "Term_user()" function.
191  *
192  * The "Term->xtra_hook" hook provides a variety of different functions,
193  * based on the first parameter (which should be taken from the various
194  * TERM_XTRA_* defines) and the second parameter (which may make sense
195  * only for some first parameters).  It is available to the program via
196  * the "Term_xtra()" function, though some first parameters are only
197  * "legal" when called from inside this package.
198  *
199  * The "Term->curs_hook" hook provides this package with a simple way
200  * to "move" or "draw" the cursor to the grid "x,y", depending on the
201  * setting of the "soft_cursor" flag.  Note that the cursor is never
202  * redrawn if "nothing" has happened to the screen (even temporarily).
203  * This hook is required.
204  *
205  * The "Term->wipe_hook" hook provides this package with a simple way
206  * to "erase", starting at "x,y", the next "n" grids.  This hook assumes
207  * that the input is valid.  This hook is required, unless the setting
208  * of the "always_pict" or "always_text" flags makes it optional.
209  *
210  * The "Term->text_hook" hook provides this package with a simple way
211  * to "draw", starting at "x,y", the "n" chars contained in "cp", using
212  * the attr "a".  This hook assumes that the input is valid, and that
213  * "n" is between 1 and 256 inclusive, but it should NOT assume that
214  * the contents of "cp" are null-terminated.  This hook is required,
215  * unless the setting of the "always_pict" flag makes it optional.
216  *
217  * The "Term->pict_hook" hook provides this package with a simple way
218  * to "draw", starting at "x,y", the "n" attr/char pairs contained in
219  * the arrays "ap" and "cp".  This hook assumes that the input is valid,
220  * and that "n" is between 1 and 256 inclusive, but it should NOT assume
221  * that the contents of "cp" are null-terminated.  This hook is optional,
222  * unless the setting of the "always_pict" or "higher_pict" flags make
223  * it required.  Note that recently, this hook was changed from taking
224  * a byte "a" and a char "c" to taking a length "n", an array of bytes
225  * "ap" and an array of chars "cp".  Old implementations of this hook
226  * should now iterate over all "n" attr/char pairs.
227  *
228  *
229  * The game "Angband" uses a set of files called "main-xxx.c", for
230  * various "xxx" suffixes.  Most of these contain a function called
231  * "init_xxx()", that will prepare the underlying visual system for
232  * use with Angband, and then create one or more "term" structures,
233  * using flags and hooks appropriate to the given platform, so that
234  * the "main()" function can call one (or more) of the "init_xxx()"
235  * functions, as appropriate, to prepare the required "term" structs
236  * (one for each desired sub-window), and these "init_xxx()" functions
237  * are called from a centralized "main()" function in "main.c".  Other
238  * "main-xxx.c" systems contain their own "main()" function which, in
239  * addition to doing everything needed to initialize the actual program,
240  * also does everything that the normal "init_xxx()" functions would do.
241  *
242  * The game "Angband" defines, in addition to "attr 0", all of the
243  * attr codes from 1 to 15, using definitions in "defines.h", and
244  * thus the "main-xxx.c" files used by Angband must handle these
245  * attr values correctly.  Also, they must handle all other attr
246  * values, though they may do so in any way they wish, for example,
247  * by always taking every attr code mod 16.  Many of the "main-xxx.c"
248  * files use "white space" ("attr 1" / "char 32") to "erase" or "clear"
249  * any window, for efficiency.
250  *
251  * The game "Angband" uses the "Term_user" hook to allow any of the
252  * "main-xxx.c" files to interact with the user, by calling this hook
253  * whenever the user presses the "!" key when the game is waiting for
254  * a new command.  This could be used, for example, to provide "unix
255  * shell commands" to the Unix versions of the game.
256  *
257  * See "main-xxx.c" for a simple skeleton file which can be used to
258  * create a "visual system" for a new platform when porting Angband.
259  */
260 
261 
262 
263 
264 
265 
266 /*
267  * The current "term"
268  */
269 term *Term = NULL;
270 
271 
272 /*** Local routines ***/
273 
274 
275 /*
276  * Nuke a term_win (see below)
277  */
term_win_nuke(term_win * s)278 static errr term_win_nuke(term_win *s)
279 {
280 	/* Free the window access arrays */
281 	KILL(s->a);
282 	KILL(s->c);
283 
284 	/* Free the window content arrays */
285 	KILL(s->va);
286 	KILL(s->vc);
287 
288 	/* Free the terrain access arrays */
289 	KILL(s->ta);
290 	KILL(s->tc);
291 
292 	/* Free the terrain content arrays */
293 	KILL(s->vta);
294 	KILL(s->vtc);
295 
296 	/* Success */
297 	return (0);
298 }
299 
300 
301 /*
302  * Initialize a "term_win" (using the given window size)
303  */
term_win_init(term_win * s,int w,int h)304 static errr term_win_init(term_win *s, int w, int h)
305 {
306 	int y;
307 
308 	/* Make the window access arrays */
309 	C_MAKE(s->a, h, byte *);
310 	C_MAKE(s->c, h, char *);
311 
312 	/* Make the window content arrays */
313 	C_MAKE(s->va, h * w, byte);
314 	C_MAKE(s->vc, h * w, char);
315 
316 	/* Make the terrain access arrays */
317 	C_MAKE(s->ta, h, byte *);
318 	C_MAKE(s->tc, h, char *);
319 
320 	/* Make the terrain content arrays */
321 	C_MAKE(s->vta, h * w, byte);
322 	C_MAKE(s->vtc, h * w, char);
323 
324 
325 	/* Prepare the window access arrays */
326 	for (y = 0; y < h; y++)
327 	{
328 		s->a[y] = s->va + w * y;
329 		s->c[y] = s->vc + w * y;
330 
331 		s->ta[y] = s->vta + w * y;
332 		s->tc[y] = s->vtc + w * y;
333 	}
334 
335 	/* Hack - disable bigtile */
336 	s->big_x1 = -1;
337 	s->big_y1 = -1;
338 	s->big_y2 = -1;
339 
340 	/* Success */
341 	return (0);
342 }
343 
344 
345 /*
346  * Copy a "term_win" from another
347  * If bigtile is set, then copy bigtile region.
348  */
term_win_copy(term_win * s,term_win * f,int w,int h)349 static errr term_win_copy(term_win *s, term_win *f, int w, int h)
350 {
351 	int x, y;
352 
353 	/* Copy contents */
354 	for (y = 0; y < h; y++)
355 	{
356 		byte *f_aa = f->a[y];
357 		char *f_cc = f->c[y];
358 
359 		byte *s_aa = s->a[y];
360 		char *s_cc = s->c[y];
361 
362 		byte *f_taa = f->ta[y];
363 		char *f_tcc = f->tc[y];
364 
365 		byte *s_taa = s->ta[y];
366 		char *s_tcc = s->tc[y];
367 
368 		for (x = 0; x < w; x++)
369 		{
370 			*s_aa++ = *f_aa++;
371 			*s_cc++ = *f_cc++;
372 
373 			*s_taa++ = *f_taa++;
374 			*s_tcc++ = *f_tcc++;
375 		}
376 	}
377 
378 	/* Copy cursor */
379 	s->cx = f->cx;
380 	s->cy = f->cy;
381 	s->cu = f->cu;
382 	s->cv = f->cv;
383 
384 	/* Copy bigtile region */
385 	s->big_x1 = f->big_x1;
386 	s->big_y1 = f->big_y1;
387 	s->big_y2 = f->big_y2;
388 
389 	/* Copy list pointer */
390 	s->next = f->next;
391 
392 	/* Success */
393 	return (0);
394 }
395 
396 
397 /*
398  * Resize a "term_win"
399  *
400  * wn, hn are the new width / height
401  * wo, ho are the old width / height
402  * win is the window to resize.
403  */
Term_resize_win(int wn,int hn,int wo,int ho,term_win * win)404 static errr Term_resize_win(int wn, int hn, int wo, int ho, term_win *win)
405 {
406 	term_win tmp;
407 
408 	/* Create a new window of the correct size */
409 	(void) term_win_init(&tmp, wn, hn);
410 
411 	/* Copy in the information from the old window */
412 	(void) term_win_copy(&tmp, win, wo, ho);
413 
414 	/* Nuke the old window */
415 	(void) term_win_nuke(win);
416 
417 	/* Illegal cursor? */
418 	if (tmp.cx >= wn) tmp.cu = 1;
419 	if (tmp.cy >= hn) tmp.cu = 1;
420 
421 	/* Save new window (Structure Copy) */
422 	*win = tmp;
423 
424 	/* Success */
425 	return (0);
426 }
427 
428 
429 /*** External hooks ***/
430 
431 
432 /*
433  * Execute the "Term->user_hook" hook, if available (see above).
434  */
Term_user(int n)435 errr Term_user(int n)
436 {
437 	/* Verify the hook */
438 	if (!Term->user_hook) return (-1);
439 
440 	/* Call the hook */
441 	return ((*Term->user_hook) (n));
442 }
443 
444 /*
445  * Execute the "Term->xtra_hook" hook, if available (see above).
446  */
Term_xtra(int n,int v)447 void Term_xtra(int n, int v)
448 {
449 	/* Verify the hook */
450 	if (!Term->xtra_hook) return;
451 
452 	/* Call the hook */
453 	(void)(*Term->xtra_hook) (n, v);
454 
455 	/* Done */
456 	return;
457 }
458 
459 
460 
461 /*** Fake hooks ***/
462 
463 
464 /*
465  * Hack -- fake hook for "Term_curs()" (see above)
466  */
Term_curs_hack(int x,int y)467 static errr Term_curs_hack(int x, int y)
468 {
469 	/* Compiler silliness */
470 	if (x || y) return (-2);
471 
472 	/* Oops */
473 	return (-1);
474 }
475 
476 /*
477  * Hack -- fake hook for "Term_wipe()" (see above)
478  */
Term_wipe_hack(int x,int y,int n)479 static errr Term_wipe_hack(int x, int y, int n)
480 {
481 	/* Compiler silliness */
482 	if (x || y || n) return (-2);
483 
484 	/* Oops */
485 	return (-1);
486 }
487 
488 /*
489  * Hack -- fake hook for "Term_text()" (see above)
490  */
Term_text_hack(int x,int y,int n,byte a,const char * cp)491 static errr Term_text_hack(int x, int y, int n, byte a, const char *cp)
492 {
493 	/* Compiler silliness */
494 	if (x || y || n || a || cp) return (-2);
495 
496 	/* Oops */
497 	return (-1);
498 }
499 
500 /*
501  * Hack -- fake hook for "Term_pict()" (see above)
502  */
Term_pict_hack(int x,int y,int n,const byte * ap,const char * cp,const byte * tap,const char * tcp)503 static errr Term_pict_hack(int x, int y, int n, const byte *ap, const char *cp,
504                            const byte *tap, const char *tcp)
505 {
506 	/* Compiler silliness */
507 	if (x || y || n || ap || cp || tap || tcp) return (-2);
508 
509 	/* Oops */
510 	return (-1);
511 }
512 
513 
514 
515 /*** Efficient routines ***/
516 
517 /*
518  * Decrease the size of the bigtile region so
519  * that it is outside the row y.
520  */
Term_bigtile_expand(int x,int y)521 static void Term_bigtile_expand(int x, int y)
522 {
523 	int sy;
524 
525 	int i;
526 
527 	/* Disable bigtile */
528 	if (Term->scr->wipe_bigtile)
529 	{
530 		/* Are we in the bigtile region? */
531 		if ((y >= Term->scr->big_y1) && (y <= Term->scr->big_y2) &&
532 				(x >= Term->scr->big_x1))
533 		{
534 			/* Save start point */
535 			sy = Term->scr->big_y1;
536 
537 			/* Move bigscreen region to below text area */
538 			Term->scr->big_y1 = y + 1;
539 
540 			/* Wipe old bigtiled region */
541 			for (i = sy; i < Term->scr->big_y1; i++)
542 			{
543 				Term_erase(Term->scr->big_x1, i, 255);
544 			}
545 
546 			/* Hack - We need to redraw everything later */
547 			Term->total_erase = TRUE;
548 		}
549 	}
550 }
551 
552 /*
553  * Mentally draw an attr/char at a given location
554  *
555  * Assumes given location and values are valid.
556  */
Term_queue_char(int x,int y,byte a,char c,byte ta,char tc)557 void Term_queue_char(int x, int y, byte a, char c, byte ta, char tc)
558 {
559 	term_win *scrn = Term->scr;
560 
561 	byte *scr_aa = &scrn->a[y][x];
562 	char *scr_cc = &scrn->c[y][x];
563 
564 	byte *scr_taa = &scrn->ta[y][x];
565 	char *scr_tcc = &scrn->tc[y][x];
566 
567 	/* Hack -- Ignore non-changes */
568 	if ((*scr_aa == a) && (*scr_cc == c) &&
569 		(*scr_taa == ta) && (*scr_tcc == tc)) return;
570 
571 	/* Save the "literal" information */
572 	*scr_aa = a;
573 	*scr_cc = c;
574 
575 	*scr_taa = ta;
576 	*scr_tcc = tc;
577 
578 	/* Check for new min/max row info */
579 	if (y < Term->y1) Term->y1 = y;
580 	if (y > Term->y2) Term->y2 = y;
581 
582 	/* Check for new min/max col info for this row */
583 	if (x < Term->x1[y]) Term->x1[y] = x;
584 	if (x > Term->x2[y]) Term->x2[y] = x;
585 }
586 
587 
588 /*
589  * Mentally draw a string of attr/chars at a given location
590  *
591  * Assumes given location and values are valid.
592  *
593  * This function is designed to be fast, with no consistancy checking.
594  * It is used to update the map in the game.
595  */
Term_queue_line(int x,int y,int n,byte * a,char * c,byte * ta,char * tc)596 void Term_queue_line(int x, int y, int n, byte *a, char *c, byte *ta, char *tc)
597 {
598 	term_win *scrn = Term->scr;
599 
600 	int x1 = -1;
601 	int x2 = -1;
602 
603 	byte *scr_aa = &scrn->a[y][x];
604 	char *scr_cc = &scrn->c[y][x];
605 
606 	byte *scr_taa = &scrn->ta[y][x];
607 	char *scr_tcc = &scrn->tc[y][x];
608 
609 	/*
610 	 * Hack - don't worry about bigtile stuff here,
611 	 * since this routine isn't used by the menues.
612 	 */
613 
614 	while (n--)
615 	{
616 		/* Hack -- Ignore non-changes */
617 		if ((*scr_aa == *a) && (*scr_cc == *c) &&
618 			(*scr_taa == *ta) && (*scr_tcc == *tc))
619 		{
620 			x++;
621 			a++;
622 			c++;
623 			ta++;
624 			tc++;
625 			scr_aa++;
626 			scr_cc++;
627 			scr_taa++;
628 			scr_tcc++;
629 			continue;
630 		}
631 
632 		/* Save the "literal" information */
633 		*scr_taa++ = *ta++;
634 		*scr_tcc++ = *tc++;
635 
636 		/* Save the "literal" information */
637 		*scr_aa++ = *a++;
638 		*scr_cc++ = *c++;
639 
640 		/* Track minimum changed column */
641 		if (x1 < 0) x1 = x;
642 
643 		/* Track maximum changed column */
644 		x2 = x;
645 
646 		x++;
647 	}
648 
649 	/* Expand the "change area" as needed */
650 	if (x1 >= 0)
651 	{
652 		/* Check for new min/max row info */
653 		if (y < Term->y1) Term->y1 = y;
654 		if (y > Term->y2) Term->y2 = y;
655 
656 		/* Check for new min/max col info in this row */
657 		if (x1 < Term->x1[y]) Term->x1[y] = x1;
658 		if (x2 > Term->x2[y]) Term->x2[y] = x2;
659 	}
660 }
661 
662 
663 /*** Refresh routines ***/
664 
665 
666 /*
667  * Flush a row of the current window (see "Term_fresh")
668  *
669  * Display text using "Term_pict()"
670  */
Term_fresh_row_pict(int y,int x1,int x2)671 static void Term_fresh_row_pict(int y, int x1, int x2)
672 {
673 	int x;
674 
675 	byte *old_aa = Term->old->a[y];
676 	char *old_cc = Term->old->c[y];
677 
678 	byte *scr_aa = Term->scr->a[y];
679 	char *scr_cc = Term->scr->c[y];
680 
681 	byte *old_taa = Term->old->ta[y];
682 	char *old_tcc = Term->old->tc[y];
683 
684 	byte *scr_taa = Term->scr->ta[y];
685 	char *scr_tcc = Term->scr->tc[y];
686 
687 	byte ota;
688 	char otc;
689 
690 	byte nta;
691 	char ntc;
692 
693 	/* Pending length */
694 	int fn = 0;
695 
696 	/* Pending start */
697 	int fx = 0;
698 
699 	byte oa;
700 	char oc;
701 
702 	byte na;
703 	char nc;
704 
705 	/* Scan "modified" columns */
706 	for (x = x1; x <= x2; x++)
707 	{
708 		/* See what is currently here */
709 		oa = old_aa[x];
710 		oc = old_cc[x];
711 
712 		/* See what is desired there */
713 		na = scr_aa[x];
714 		nc = scr_cc[x];
715 
716 		ota = old_taa[x];
717 		otc = old_tcc[x];
718 
719 		nta = scr_taa[x];
720 		ntc = scr_tcc[x];
721 
722 		/* Handle unchanged grids */
723 		if ((na == oa) && (nc == oc) && (nta == ota) && (ntc == otc)
724 			&& !Term->total_erase)
725 		{
726 			/* Flush */
727 			if (fn)
728 			{
729 				/* Draw pending attr/char pairs */
730 				(void)((*Term->pict_hook) (fx, y, fn,
731 										   &scr_aa[fx], &scr_cc[fx],
732 										   &scr_taa[fx], &scr_tcc[fx]));
733 
734 				/* Forget */
735 				fn = 0;
736 			}
737 
738 			/* Skip */
739 			continue;
740 		}
741 		/* Save new contents */
742 		old_aa[x] = na;
743 		old_cc[x] = nc;
744 
745 		old_taa[x] = nta;
746 		old_tcc[x] = ntc;
747 
748 		/* Restart and Advance */
749 		if (fn++ == 0) fx = x;
750 	}
751 
752 	/* Flush */
753 	if (fn)
754 	{
755 		/* Draw pending attr/char pairs */
756 		(void)((*Term->pict_hook) (fx, y, fn,
757 								   &scr_aa[fx], &scr_cc[fx], &scr_taa[fx],
758 								   &scr_tcc[fx]));
759 	}
760 }
761 
762 
763 
764 /*
765  * Flush a row of the current window (see "Term_fresh")
766  *
767  * Display text using "Term_text()" and "Term_wipe()",
768  * but use "Term_pict()" for high-bit attr/char pairs
769  */
Term_fresh_row_both(int y,int x1,int x2)770 static void Term_fresh_row_both(int y, int x1, int x2)
771 {
772 	int x;
773 
774 	byte *old_aa = Term->old->a[y];
775 	char *old_cc = Term->old->c[y];
776 
777 	byte *scr_aa = Term->scr->a[y];
778 	char *scr_cc = Term->scr->c[y];
779 
780 	byte *old_taa = Term->old->ta[y];
781 	char *old_tcc = Term->old->tc[y];
782 	byte *scr_taa = Term->scr->ta[y];
783 	char *scr_tcc = Term->scr->tc[y];
784 
785 	byte ota;
786 	char otc;
787 	byte nta;
788 	char ntc;
789 
790 	/* The "always_text" flag */
791 	int always_text = Term->always_text;
792 
793 	/* Pending length */
794 	int fn = 0;
795 
796 	/* Pending start */
797 	int fx = 0;
798 
799 	/* Pending attr */
800 	byte fa = Term->attr_blank;
801 
802 	byte oa;
803 	char oc;
804 
805 	byte na;
806 	char nc;
807 
808 	/* Scan "modified" columns */
809 	for (x = x1; x <= x2; x++)
810 	{
811 		/* See what is currently here */
812 		oa = old_aa[x];
813 		oc = old_cc[x];
814 
815 		/* See what is desired there */
816 		na = scr_aa[x];
817 		nc = scr_cc[x];
818 
819 		ota = old_taa[x];
820 		otc = old_tcc[x];
821 
822 		nta = scr_taa[x];
823 		ntc = scr_tcc[x];
824 
825 		/* Handle unchanged grids */
826 		if ((na == oa) && (nc == oc) && (nta == ota) && (ntc == otc)
827 			&& !Term->total_erase)
828 		{
829 			/* Flush */
830 			if (fn)
831 			{
832 				/* Draw pending chars (normal) */
833 				if (fa || always_text)
834 				{
835 					(void)((*Term->text_hook) (fx, y, fn, fa, &scr_cc[fx]));
836 				}
837 				/* Draw pending chars (black) */
838 				else
839 				{
840 					(void)((*Term->wipe_hook) (fx, y, fn));
841 				}
842 				/* Forget */
843 				fn = 0;
844 			}
845 
846 			/* Skip */
847 			continue;
848 		}
849 
850 		/* Save new contents */
851 		old_aa[x] = na;
852 		old_cc[x] = nc;
853 
854 		old_taa[x] = nta;
855 		old_tcc[x] = ntc;
856 
857 		/* Handle high-bit attr/chars */
858 		if (na & 0x80)
859 		{
860 			/* Flush */
861 			if (fn)
862 			{
863 				/* Draw pending chars (normal) */
864 				if (fa || always_text)
865 				{
866 					(void)((*Term->text_hook) (fx, y, fn, fa, &scr_cc[fx]));
867 				}
868 				/* Draw pending chars (black) */
869 				else
870 				{
871 					(void)((*Term->wipe_hook) (fx, y, fn));
872 				}
873 				/* Forget */
874 				fn = 0;
875 			}
876 
877 			/* Hack -- Draw the special attr/char pair */
878 			(void)((*Term->pict_hook) (x, y, 1, &na, &nc, &nta, &ntc));
879 
880 			/* Skip */
881 			continue;
882 		}
883 
884 		/* Notice new color */
885 		if (fa != na)
886 		{
887 			/* Flush */
888 			if (fn)
889 			{
890 				/* Draw the pending chars */
891 				if (fa || always_text)
892 				{
893 					(void)((*Term->text_hook) (fx, y, fn, fa, &scr_cc[fx]));
894 				}
895 				/* Hack -- Erase "leading" spaces */
896 				else
897 				{
898 					(void)((*Term->wipe_hook) (fx, y, fn));
899 				}
900 				/* Forget */
901 				fn = 0;
902 			}
903 
904 			/* Save the new color */
905 			fa = na;
906 		}
907 
908 		/* Restart and Advance */
909 		if (fn++ == 0) fx = x;
910 	}
911 
912 	/* Flush */
913 	if (fn)
914 	{
915 		/* Draw pending chars (normal) */
916 		if (fa || always_text)
917 		{
918 			(void)((*Term->text_hook) (fx, y, fn, fa, &scr_cc[fx]));
919 		}
920 		/* Draw pending chars (black) */
921 		else
922 		{
923 			(void)((*Term->wipe_hook) (fx, y, fn));
924 		}
925 	}
926 }
927 
928 
929 /*
930  * Flush a row of the current window (see "Term_fresh")
931  *
932  * Display text using "Term_text()" and "Term_wipe()"
933  */
Term_fresh_row_text(int y,int x1,int x2)934 static void Term_fresh_row_text(int y, int x1, int x2)
935 {
936 	int x;
937 
938 	byte *old_aa = Term->old->a[y];
939 	char *old_cc = Term->old->c[y];
940 
941 	byte *scr_aa = Term->scr->a[y];
942 	char *scr_cc = Term->scr->c[y];
943 
944 	/* The "always_text" flag */
945 	int always_text = Term->always_text;
946 
947 	/* Pending length */
948 	int fn = 0;
949 
950 	/* Pending start */
951 	int fx = 0;
952 
953 	/* Pending attr */
954 	byte fa = Term->attr_blank;
955 
956 	byte oa;
957 	char oc;
958 
959 	byte na;
960 	char nc;
961 
962 
963 	/* Scan "modified" columns */
964 	for (x = x1; x <= x2; x++)
965 	{
966 		/* See what is currently here */
967 		oa = old_aa[x];
968 		oc = old_cc[x];
969 
970 		/* See what is desired there */
971 		na = scr_aa[x];
972 		nc = scr_cc[x];
973 
974 		/* Handle unchanged grids */
975 		if ((na == oa) && (nc == oc) && !Term->total_erase)
976 		{
977 			/* Flush */
978 			if (fn)
979 			{
980 				/* Draw pending chars (normal) */
981 				if (fa || always_text)
982 				{
983 					(void)((*Term->text_hook) (fx, y, fn, fa, &scr_cc[fx]));
984 				}
985 
986 				/* Draw pending chars (black) */
987 				else
988 				{
989 					(void)((*Term->wipe_hook) (fx, y, fn));
990 				}
991 
992 				/* Forget */
993 				fn = 0;
994 			}
995 
996 			/* Skip */
997 			continue;
998 		}
999 
1000 		/* Save new contents */
1001 		old_aa[x] = na;
1002 		old_cc[x] = nc;
1003 
1004 		/* Notice new color */
1005 		if (fa != na)
1006 		{
1007 			/* Flush */
1008 			if (fn)
1009 			{
1010 				/* Draw the pending chars */
1011 				if (fa || always_text)
1012 				{
1013 					(void)((*Term->text_hook) (fx, y, fn, fa, &scr_cc[fx]));
1014 				}
1015 
1016 				/* Hack -- Erase "leading" spaces */
1017 				else
1018 				{
1019 					(void)((*Term->wipe_hook) (fx, y, fn));
1020 				}
1021 
1022 				/* Forget */
1023 				fn = 0;
1024 			}
1025 
1026 			/* Save the new color */
1027 			fa = na;
1028 		}
1029 
1030 		/* Restart and Advance */
1031 		if (fn++ == 0) fx = x;
1032 	}
1033 
1034 	/* Flush */
1035 	if (fn)
1036 	{
1037 		/* Draw pending chars (normal) */
1038 		if (fa || always_text)
1039 		{
1040 			(void)((*Term->text_hook) (fx, y, fn, fa, &scr_cc[fx]));
1041 		}
1042 
1043 		/* Draw pending chars (black) */
1044 		else
1045 		{
1046 			(void)((*Term->wipe_hook) (fx, y, fn));
1047 		}
1048 	}
1049 }
1050 
1051 
1052 /*
1053  * Draw / Redraw the tiles in a term
1054  */
Term_fresh_section(void)1055 static void Term_fresh_section(void)
1056 {
1057 	int y;
1058 	int y1 = Term->y1;
1059 	int y2 = Term->y2;
1060 
1061 	int w = Term->wid;
1062 	int h = Term->hgt;
1063 
1064 	/* Something to update */
1065 	if (y1 <= y2)
1066 	{
1067 		/* Handle "icky corner" */
1068 		if (Term->icky_corner)
1069 		{
1070 			/* Avoid the corner */
1071 			if (y2 >= h - 1)
1072 			{
1073 				/* Avoid the corner */
1074 				if (Term->x2[h - 1] > w - 2)
1075 				{
1076 					/* Avoid the corner */
1077 					Term->x2[h - 1] = w - 2;
1078 				}
1079 			}
1080 		}
1081 
1082 
1083 		/* Scan the "modified" rows */
1084 		for (y = y1; y <= y2; ++y)
1085 		{
1086 			int x1 = Term->x1[y];
1087 			int x2 = Term->x2[y];
1088 
1089 			/* Flush each "modified" row */
1090 			if (x1 <= x2)
1091 			{
1092 				/* Always use "Term_pict()" */
1093 				if (Term->always_pict)
1094 				{
1095 					/* Flush the row */
1096 					Term_fresh_row_pict(y, x1, x2);
1097 				}
1098 
1099 				/* Sometimes use "Term_pict()" */
1100 				else if (Term->higher_pict)
1101 				{
1102 					/* Flush the row */
1103 					Term_fresh_row_both(y, x1, x2);
1104 				}
1105 
1106 				/* Never use "Term_pict()" */
1107 				else
1108 				{
1109 					/* Flush the row */
1110 					Term_fresh_row_text(y, x1, x2);
1111 				}
1112 
1113 				/* This row is all done */
1114 				Term->x1[y] = w;
1115 				Term->x2[y] = 0;
1116 
1117 				/* Hack -- Flush that row (if allowed) */
1118 				if (!Term->never_frosh) Term_xtra(TERM_XTRA_FROSH, y);
1119 			}
1120 		}
1121 
1122 		/* No rows are invalid */
1123 		Term->y1 = h;
1124 		Term->y2 = 0;
1125 	}
1126 }
1127 
1128 /*
1129  * Redraw the cursor
1130  */
Term_fresh_cursor(void)1131 static void Term_fresh_cursor(void)
1132 {
1133 	term_win *old = Term->old;
1134 	term_win *scr = Term->scr;
1135 
1136 	/* Cursor update -- Show new Cursor */
1137 	if (Term->soft_cursor)
1138 	{
1139 		/* Draw the cursor */
1140 		if (!scr->cu && scr->cv)
1141 		{
1142 			/* Call the cursor display routine */
1143 			(void)((*Term->curs_hook) (scr->cx, scr->cy));
1144 		}
1145 	}
1146 
1147 	/* Cursor Update -- Show new Cursor */
1148 	else
1149 	{
1150 		/* The cursor is useless, hide it */
1151 		if (scr->cu)
1152 		{
1153 			/* Paranoia -- Put the cursor NEAR where it belongs */
1154 			(void)((*Term->curs_hook) (Term->wid - 1, scr->cy));
1155 
1156 			/* Make the cursor invisible */
1157 			/* Term_xtra(TERM_XTRA_SHAPE, 0); */
1158 		}
1159 
1160 		/* The cursor is invisible, hide it */
1161 		else if (!scr->cv)
1162 		{
1163 			/* Paranoia -- Put the cursor where it belongs */
1164 			(void)((*Term->curs_hook) (scr->cx, scr->cy));
1165 
1166 			/* Make the cursor invisible */
1167 			/* Term_xtra(TERM_XTRA_SHAPE, 0); */
1168 		}
1169 
1170 		/* The cursor is visible, display it correctly */
1171 		else
1172 		{
1173 			/* Put the cursor where it belongs */
1174 			(void)((*Term->curs_hook) (scr->cx, scr->cy));
1175 
1176 			/* Make the cursor visible */
1177 			Term_xtra(TERM_XTRA_SHAPE, 1);
1178 		}
1179 	}
1180 
1181 
1182 	/* Save the "cursor state" */
1183 	old->cu = scr->cu;
1184 	old->cv = scr->cv;
1185 	old->cx = scr->cx;
1186 	old->cy = scr->cy;
1187 }
1188 
1189 /*
1190  * Actually perform all requested changes to the window
1191  *
1192  * If absolutely nothing has changed, not even temporarily, or if the
1193  * current "Term" is not mapped, then this function will return 1 and
1194  * do absolutely nothing.
1195  *
1196  * Note that when "soft_cursor" is true, we erase the cursor (if needed)
1197  * whenever anything has changed, and redraw it (if needed) after all of
1198  * the screen updates are complete.  This will induce a small amount of
1199  * "cursor flicker" but only when the screen has been updated.  If the
1200  * screen is updated and then restored, you may still get this flicker.
1201  *
1202  * When "soft_cursor" is not true, we make the cursor invisible before
1203  * doing anything else if it is supposed to be invisible by the time we
1204  * are done, and we make it visible after moving it to its final location
1205  * after all of the screen updates are complete.
1206  *
1207  * Note that "Term_xtra(TERM_XTRA_FROSH,y)" will be always be called
1208  * after any row "y" has been "flushed", unless the "Term->never_frosh"
1209  * flag is set, and "Term_xtra(TERM_XTRA_FRESH,0)" will be called after
1210  * all of the rows have been "flushed".
1211  *
1212  * Note the use of three different functions to handle the actual flush,
1213  * based on the settings of the "Term->always_pict" and "Term->higher_pict"
1214  * flags (see below).
1215  *
1216  * The three helper functions (above) work by collecting similar adjacent
1217  * grids into stripes, and then sending each stripe to "Term->pict_hook",
1218  * "Term->text_hook", or "Term->wipe_hook", based on the settings of the
1219  * "Term->always_pict" and "Term->higher_pict" flags, which select which
1220  * of the helper functions to call to flush each row.
1221  *
1222  * The helper functions currently "skip" any grids which already contain
1223  * the desired contents.  This may or may not be the best method, especially
1224  * when the desired content fits nicely into the current stripe.  For example,
1225  * it might be better to go ahead and queue them while allowed, but keep a
1226  * count of the "trailing skipables", then, when time to flush, or when a
1227  * "non skippable" is found, force a flush if there are too many skippables.
1228  *
1229  * Perhaps an "initialization" stage, where the "text" (and "attr")
1230  * buffers are "filled" with information, converting "blanks" into
1231  * a convenient representation, and marking "skips" with "zero chars",
1232  * and then some "processing" is done to determine which chars to skip.
1233  *
1234  * Currently, the helper functions are optimal for systems which prefer
1235  * to "print a char + move a char + print a char" to "print three chars",
1236  * and for applications that do a lot of "detailed" color printing.
1237  *
1238  * In the two "queue" functions, total "non-changes" are "pre-skipped".
1239  * The helper functions must also handle situations in which the contents
1240  * of a grid are changed, but then changed back to the original value,
1241  * and situations in which two grids in the same row are changed, but
1242  * the grids between them are unchanged.
1243  *
1244  * If the "Term->always_pict" flag is set, then "Term_fresh_row_pict()"
1245  * will be used instead of "Term_fresh_row_text()".  This allows all the
1246  * modified grids to be collected into stripes of attr/char pairs, which
1247  * are then sent to the "Term->pict_hook" hook, which can draw these pairs
1248  * in whatever way it would like.
1249  *
1250  * If the "Term->higher_pict" flag is set, then "Term_fresh_row_both()"
1251  * will be used instead of "Term_fresh_row_text()".  This allows all the
1252  * "special" attr/char pairs (in which both the attr and char have the
1253  * high-bit set) to be sent (one pair at a time) to the "Term->pict_hook"
1254  * hook, which can draw these pairs in whatever way it would like.
1255  *
1256  * Normally, the "Term_wipe()" function is used only to display "blanks"
1257  * that were induced by "Term_clear()" or "()", and then only
1258  * if the "attr_blank" and "char_blank" fields have not been redefined
1259  * to use "white space" instead of the default "black space".  Actually,
1260  * the "Term_wipe()" function is used to display all "black" text, such
1261  * as the default "spaces" created by "Term_clear()" and "Term_erase()".
1262  *
1263  * Note that the "Term->always_text" flag will disable the use of the
1264  * "Term_wipe()" function hook entirely, and force all text, even text
1265  * drawn in the color "black", to be explicitly drawn.  This is useful
1266  * for machines which implement "Term_wipe()" by just drawing spaces.
1267  *
1268  * Note that the "Term->always_pict" flag will disable the use of the
1269  * "Term_wipe()" function entirely, and force everything, even text
1270  * drawn in the attr "black", to be explicitly drawn.
1271  *
1272  * Note that if no "black" text is ever drawn, and if "attr_blank" is
1273  * not "zero", then the "Term_wipe" hook will never be used, even if
1274  * the "Term->always_text" flag is not set.
1275  *
1276  * This function does nothing unless the "Term" is "mapped", which allows
1277  * certain systems to optimize the handling of "closed" windows.
1278  *
1279  * On systems with a "soft" cursor, we must explicitly erase the cursor
1280  * before flushing the output, if needed, to prevent a "jumpy" refresh.
1281  * The actual method for this is horrible, but there is very little that
1282  * we can do to simplify it efficiently.  XXX XXX XXX
1283  *
1284  * On systems with a "hard" cursor, we will "hide" the cursor before
1285  * flushing the output, if needed, to avoid a "flickery" refresh.  It
1286  * would be nice to *always* hide the cursor during the refresh, but
1287  * this might be expensive (and/or ugly) on some machines.
1288  *
1289  * The "Term->icky_corner" flag is used to avoid calling "Term_wipe()"
1290  * or "Term_pict()" or "Term_text()" on the bottom right corner of the
1291  * window, which might induce "scrolling" or other nasty stuff on old
1292  * dumb terminals.  This flag is handled very efficiently.  We assume
1293  * that the "Term_curs()" call will prevent placing the cursor in the
1294  * corner, if needed, though I doubt such placement is ever a problem.
1295  * Currently, the use of "Term->icky_corner" and "Term->soft_cursor"
1296  * together may result in undefined behavior.
1297  */
Term_fresh(void)1298 void Term_fresh(void)
1299 {
1300 	int y;
1301 
1302 	int w = Term->wid;
1303 	int h = Term->hgt;
1304 
1305 	int y1 = Term->y1;
1306 	int y2 = Term->y2;
1307 
1308 	term_win *old = Term->old;
1309 	term_win *scr = Term->scr;
1310 
1311 
1312 	/* Do nothing unless "mapped" */
1313 	if (!Term->mapped_flag) return;
1314 
1315 
1316 	/* Trivial Refresh */
1317 	if ((y1 > y2) &&
1318 		(scr->cu == old->cu) &&
1319 		(scr->cv == old->cv) &&
1320 		(scr->cx == old->cx) && (scr->cy == old->cy) && !(Term->total_erase))
1321 	{
1322 		/* Nothing */
1323 		return;
1324 	}
1325 
1326 	/* Paranoia -- use "fake" hooks to prevent core dumps */
1327 	if (!Term->curs_hook) Term->curs_hook = Term_curs_hack;
1328 	if (!Term->wipe_hook) Term->wipe_hook = Term_wipe_hack;
1329 	if (!Term->text_hook) Term->text_hook = Term_text_hack;
1330 	if (!Term->pict_hook) Term->pict_hook = Term_pict_hack;
1331 
1332 	/* Handle "total erase" */
1333 	if (Term->total_erase)
1334 	{
1335 		/* Hack -- clear all "cursor" data */
1336 		old->cv = old->cu = old->cx = old->cy = 0;
1337 
1338 		/* Redraw every row */
1339 		Term->y1 = y1 = 0;
1340 		Term->y2 = y2 = h - 1;
1341 
1342 		/* Redraw every column */
1343 		for (y = 0; y < h; y++)
1344 		{
1345 			Term->x1[y] = 0;
1346 			Term->x2[y] = w - 1;
1347 		}
1348 	}
1349 
1350 
1351 	/* Cursor update -- Erase old Cursor */
1352 	if (Term->soft_cursor)
1353 	{
1354 		/* Cursor was visible */
1355 		if (!old->cu && old->cv)
1356 		{
1357 			int tx = old->cx;
1358 			int ty = old->cy;
1359 
1360 			byte *old_aa = old->a[ty];
1361 			char *old_cc = old->c[ty];
1362 
1363 			byte oa = old_aa[tx];
1364 			char oc = old_cc[tx];
1365 
1366 			byte *old_taa = old->ta[ty];
1367 			char *old_tcc = old->tc[ty];
1368 
1369 			byte ota = old_taa[tx];
1370 			char otc = old_tcc[tx];
1371 
1372 			/* Hack -- use "Term_pict()" always */
1373 			if (Term->always_pict)
1374 			{
1375 				(void)((*Term->pict_hook) (tx, ty, 1, &oa, &oc, &ota, &otc));
1376 			}
1377 
1378 			/* Hack -- use "Term_pict()" sometimes */
1379 			else if (Term->higher_pict && (oa & 0x80))
1380 			{
1381 				(void)((*Term->pict_hook) (tx, ty, 1, &oa, &oc, &ota, &otc));
1382 			}
1383 
1384 			/* Hack -- restore the actual character */
1385 			else if (oa || Term->always_text)
1386 			{
1387 				(void)((*Term->text_hook) (tx, ty, 1, oa, &oc));
1388 			}
1389 
1390 			/* Hack -- erase the grid */
1391 			else
1392 			{
1393 				(void)((*Term->wipe_hook) (tx, ty, 1));
1394 			}
1395 		}
1396 	}
1397 
1398 	/* Cursor Update -- Erase old Cursor */
1399 	else
1400 	{
1401 		/* Cursor will be invisible */
1402 		if (scr->cu || !scr->cv)
1403 		{
1404 			/* Make the cursor invisible */
1405 			Term_xtra(TERM_XTRA_SHAPE, 0);
1406 		}
1407 	}
1408 
1409 	/* Redraw stuff as required */
1410 	Term_fresh_section();
1411 
1412 	/* Redraw cursor */
1413 	Term_fresh_cursor();
1414 
1415 	/* Actually flush the output */
1416 	Term_xtra(TERM_XTRA_FRESH, 0);
1417 
1418 	/* Forget "total erase" */
1419 	Term->total_erase = FALSE;
1420 
1421 	/* Success */
1422 	return;
1423 }
1424 
1425 
1426 
1427 /*** Output routines ***/
1428 
1429 
1430 /*
1431  * Set the cursor visibility
1432  */
Term_set_cursor(int v)1433 errr Term_set_cursor(int v)
1434 {
1435 	/* Already done */
1436 	if (Term->scr->cv == v) return (1);
1437 
1438 	/* Change */
1439 	Term->scr->cv = v;
1440 
1441 	/* Success */
1442 	return (0);
1443 }
1444 
1445 
1446 /*
1447  * Place the cursor at a given location
1448  *
1449  * Note -- "illegal" requests do not move the cursor.
1450  */
Term_gotoxy(int x,int y)1451 void Term_gotoxy(int x, int y)
1452 {
1453 	int w = Term->wid;
1454 	int h = Term->hgt;
1455 
1456 	/* Verify */
1457 	if ((x < 0) || (x >= w)) return;
1458 	if ((y < 0) || (y >= h)) return;
1459 
1460 	/* Remember the cursor */
1461 	Term->scr->cx = x;
1462 	Term->scr->cy = y;
1463 
1464 	/* The cursor is not useless */
1465 	Term->scr->cu = 0;
1466 
1467 	/* Success */
1468 	return;
1469 }
1470 
1471 
1472 /*
1473  * At a given location, place an attr/char
1474  * Do not change the cursor position
1475  * No visual changes until "Term_fresh()".
1476  */
Term_draw(int x,int y,byte a,char c)1477 void Term_draw(int x, int y, byte a, char c)
1478 {
1479 	int w = Term->wid;
1480 	int h = Term->hgt;
1481 
1482 	/* Verify location */
1483 	if ((x < 0) || (x >= w)) return;
1484 	if ((y < 0) || (y >= h)) return;
1485 
1486 	/* Paranoia -- illegal char */
1487 	if (!c) return;
1488 
1489 	/* Notice bigtile region changes */
1490 	Term_bigtile_expand(x, y);
1491 
1492 	/* Queue it for later */
1493 	Term_queue_char(x, y, a, c, 0, 0);
1494 
1495 	/* Success */
1496 	return;
1497 }
1498 
1499 
1500 /*
1501  * Using the given attr, add the given char at the cursor.
1502  *
1503  * We queue the given attr/char for display at the current
1504  * cursor location, and advance the cursor to the right,
1505  * marking it as unuable and returning "1" if it leaves
1506  * the screen, and otherwise returning "0".
1507  *
1508  * So when this function, or the following one, return a
1509  * positive value, future calls to either function will
1510  * return negative ones.
1511  */
Term_addch(byte a,char c)1512 void Term_addch(byte a, char c)
1513 {
1514 	int w = Term->wid;
1515 
1516 	/* Handle "unusable" cursor */
1517 	if (Term->scr->cu) return;
1518 
1519 	/* Paranoia -- no illegal chars */
1520 	if (!c) return;
1521 
1522 	/* Notice bigtile region changes */
1523 	Term_bigtile_expand(Term->scr->cx, Term->scr->cy);
1524 
1525 	/* Queue the given character for display */
1526 	Term_queue_char(Term->scr->cx, Term->scr->cy, a, c, 0, 0);
1527 
1528 	/* Advance the cursor */
1529 	Term->scr->cx++;
1530 
1531 	/* Success */
1532 	if (Term->scr->cx < w) return;
1533 
1534 	/* Note "Useless" cursor */
1535 	Term->scr->cu = 1;
1536 
1537 	/* Note "Useless" cursor */
1538 	return;
1539 }
1540 
1541 
1542 /*
1543  * Move to a location and, using an attr, add a char
1544  */
Term_putch(int x,int y,byte a,char c)1545 void Term_putch(int x, int y, byte a, char c)
1546 {
1547 	/* Move first */
1548 	Term_gotoxy(x, y);
1549 
1550 	/* Then add the char */
1551 	Term_addch(a, c);
1552 }
1553 
1554 
1555 /*
1556  * Place cursor at (x,y), and clear the next "n" chars
1557  */
Term_erase(int x,int y,int n)1558 void Term_erase(int x, int y, int n)
1559 {
1560 	int i;
1561 
1562 	int w = Term->wid;
1563 	int h = Term->hgt;
1564 
1565 	int x1 = -1;
1566 	int x2 = -1;
1567 
1568 	int na = Term->attr_blank;
1569 	int nc = Term->char_blank;
1570 
1571 	byte *scr_aa;
1572 	char *scr_cc;
1573 
1574 	byte *scr_taa;
1575 	char *scr_tcc;
1576 
1577 	/* Place cursor */
1578 	Term_gotoxy(x, y);
1579 
1580 	/* Paranoia */
1581 	if (n < 0) n = 0;
1582 	if ((x >= w) || (y >= h)) return;
1583 
1584 	/* Force legal size */
1585 	if (x + n > w) n = w - x;
1586 
1587 	/* Update bigtile info */
1588 	Term_bigtile_expand(x, y);
1589 
1590 	/* Fast access */
1591 	scr_aa = Term->scr->a[y];
1592 	scr_cc = Term->scr->c[y];
1593 
1594 	scr_taa = Term->scr->ta[y];
1595 	scr_tcc = Term->scr->tc[y];
1596 
1597 	/* Scan every column */
1598 	for (i = 0; i < n; i++, x++)
1599 	{
1600 		int oa = scr_aa[x];
1601 		int oc = scr_cc[x];
1602 
1603 		/* Hack -- Ignore "non-changes" */
1604 		if ((oa == na) && (oc == nc)) continue;
1605 
1606 		/* Save the "literal" information */
1607 		scr_aa[x] = na;
1608 		scr_cc[x] = nc;
1609 
1610 		scr_taa[x] = 0;
1611 		scr_tcc[x] = 0;
1612 
1613 		/* Track minimum changed column */
1614 		if (x1 < 0) x1 = x;
1615 
1616 		/* Track maximum changed column */
1617 		x2 = x;
1618 	}
1619 
1620 	/* Expand the "change area" as needed */
1621 	if (x1 >= 0)
1622 	{
1623 		/* Check for new min/max row info */
1624 		if (y < Term->y1) Term->y1 = y;
1625 		if (y > Term->y2) Term->y2 = y;
1626 
1627 		/* Check for new min/max col info in this row */
1628 		if (x1 < Term->x1[y]) Term->x1[y] = x1;
1629 		if (x2 > Term->x2[y]) Term->x2[y] = x2;
1630 	}
1631 }
1632 
1633 
1634 /*
1635  * Clear the entire window, and move to the top left corner
1636  *
1637  * Note the use of the special "total_erase" code
1638  */
Term_clear(void)1639 void Term_clear(void)
1640 {
1641 	int x, y;
1642 
1643 	int w = Term->wid;
1644 	int h = Term->hgt;
1645 
1646 	byte na = Term->attr_blank;
1647 	char nc = Term->char_blank;
1648 
1649 	/* Cursor usable */
1650 	Term->scr->cu = 0;
1651 
1652 	/* Cursor to the top left */
1653 	Term->scr->cx = Term->scr->cy = 0;
1654 
1655 	/* Wipe each row */
1656 	for (y = 0; y < h; y++)
1657 	{
1658 		byte *scr_aa = Term->scr->a[y];
1659 		char *scr_cc = Term->scr->c[y];
1660 
1661 		byte *scr_taa = Term->scr->ta[y];
1662 		char *scr_tcc = Term->scr->tc[y];
1663 
1664 		/* Wipe each column */
1665 		for (x = 0; x < w; x++)
1666 		{
1667 			scr_aa[x] = na;
1668 			scr_cc[x] = nc;
1669 
1670 			scr_taa[x] = 0;
1671 			scr_tcc[x] = 0;
1672 		}
1673 
1674 		/* This row has changed */
1675 		Term->x1[y] = 0;
1676 		Term->x2[y] = w - 1;
1677 	}
1678 
1679 	/* Every row has changed */
1680 	Term->y1 = 0;
1681 	Term->y2 = h - 1;
1682 
1683 	/* Force "total erase" */
1684 	Term->total_erase = TRUE;
1685 }
1686 
1687 
1688 
1689 /*
1690  * Redraw (and refresh) the whole window.
1691  */
Term_redraw(void)1692 void Term_redraw(void)
1693 {
1694 	/* Force "total erase" */
1695 	Term->total_erase = TRUE;
1696 
1697 	/* Hack -- Refresh */
1698 	Term_fresh();
1699 }
1700 
1701 
1702 /*
1703  * Redraw part of a window.
1704  */
Term_redraw_section(int x1,int y1,int x2,int y2)1705 errr Term_redraw_section(int x1, int y1, int x2, int y2)
1706 {
1707 	int i;
1708 
1709 	bool redraw_cursor = FALSE;
1710 
1711 	term_win *old = Term->old;
1712 
1713 	/* Hack - make bigtile work */
1714 	if (Term->scr->big_x1 != -1)
1715 	{
1716 		x1 = (x1 - 1) / 2;
1717 		x2 = x2 * 2;
1718 	}
1719 
1720 	/* Bounds checking */
1721 	if (y2 >= Term->hgt) y2 = Term->hgt - 1;
1722 	if (x2 >= Term->wid) x2 = Term->wid - 1;
1723 	if (y1 < 0) y1 = 0;
1724 	if (x1 < 0) x1 = 0;
1725 
1726 	/* Set y limits */
1727 	if (Term->y1 > y1) Term->y1 = y1;
1728 	if (Term->y2 < y2) Term->y2 = y2;
1729 
1730 	/* Set the x limits */
1731 	for (i = y1; i <= y2; i++)
1732 	{
1733 		if (Term->x1[i] > x1) Term->x1[i] = x1;
1734 		if (Term->x2[i] < x2) Term->x2[i] = x2;
1735 	}
1736 
1737 	if ((old->cx >= Term->x1[old->cy]) && (old->cx <= Term->x2[old->cy]) &&
1738 		(old->cy >= Term->y1) && (old->cy <= Term->y2))
1739 	{
1740 		/* Hack -- clear all "cursor" data */
1741 		old->cv = 0;
1742 		old->cu = 0;
1743 		old->cx = 0;
1744 		old->cy = 0;
1745 
1746 		redraw_cursor = TRUE;
1747 	}
1748 
1749 	/* Refresh */
1750 	Term->total_erase = TRUE;
1751 	Term_fresh_section();
1752 	Term->total_erase = FALSE;
1753 
1754 	if (redraw_cursor) Term_fresh_cursor();
1755 
1756 	/* Actually flush the output */
1757 	Term_xtra(TERM_XTRA_FRESH, 0);
1758 
1759 	/* Success */
1760 	return (0);
1761 }
1762 
1763 
1764 
1765 /*** Access routines ***/
1766 
1767 
1768 /*
1769  * Extract the cursor visibility
1770  */
Term_get_cursor(int * v)1771 errr Term_get_cursor(int *v)
1772 {
1773 	/* Extract visibility */
1774 	(*v) = Term->scr->cv;
1775 
1776 	/* Success */
1777 	return (0);
1778 }
1779 
1780 
1781 /*
1782  * Extract the current window size
1783  */
Term_get_size(int * w,int * h)1784 void Term_get_size(int *w, int *h)
1785 {
1786 	/* Access the cursor */
1787 	(*w) = Term->wid;
1788 	(*h) = Term->hgt;
1789 }
1790 
1791 
1792 /*
1793  * Extract the current cursor location
1794  */
Term_locate(int * x,int * y)1795 errr Term_locate(int *x, int *y)
1796 {
1797 	/* Access the cursor */
1798 	(*x) = Term->scr->cx;
1799 	(*y) = Term->scr->cy;
1800 
1801 	/* Warn about "useless" cursor */
1802 	if (Term->scr->cu) return (1);
1803 
1804 	/* Success */
1805 	return (0);
1806 }
1807 
1808 
1809 /*
1810  * At a given location, determine the "current" attr and char
1811  * Note that this refers to what will be on the window after the
1812  * next call to "Term_fresh()".  It may or may not already be there.
1813  */
Term_what(int x,int y,byte * a,char * c)1814 errr Term_what(int x, int y, byte *a, char *c)
1815 {
1816 	int w = Term->wid;
1817 	int h = Term->hgt;
1818 
1819 	/* Verify location */
1820 	if ((x < 0) || (x >= w)) return (-1);
1821 	if ((y < 0) || (y >= h)) return (-1);
1822 
1823 	/* Direct access */
1824 	(*a) = Term->scr->a[y][x];
1825 	(*c) = Term->scr->c[y][x];
1826 
1827 	/* Success */
1828 	return (0);
1829 }
1830 
1831 
1832 
1833 /*** Input routines ***/
1834 
1835 
1836 /*
1837  * Flush and forget the input
1838  */
Term_flush(void)1839 void Term_flush(void)
1840 {
1841 	/* Hack -- Flush all events */
1842 	Term_xtra(TERM_XTRA_FLUSH, 0);
1843 
1844 	/* Forget all keypresses */
1845 	Term->key_head = Term->key_tail = 0;
1846 }
1847 
1848 
1849 
1850 /*
1851  * Add a keypress to the "queue"
1852  */
Term_keypress(int k)1853 errr Term_keypress(int k)
1854 {
1855 	/* Hack -- Refuse to enqueue non-keys */
1856 	if (!k) return (-1);
1857 
1858 	/* Store the char, advance the queue */
1859 	Term->key_queue[Term->key_head++] = k;
1860 
1861 	/* Circular queue, handle wrap */
1862 	if (Term->key_head == Term->key_size) Term->key_head = 0;
1863 
1864 	/* Success (unless overflow) */
1865 	if (Term->key_head != Term->key_tail) return (0);
1866 
1867 #if 0
1868 	/* Hack -- Forget the oldest key */
1869 	if (++Term->key_tail == Term->key_size) Term->key_tail = 0;
1870 #endif
1871 
1872 	/* Problem */
1873 	return (1);
1874 }
1875 
1876 
1877 /*
1878  * Add a keypress to the FRONT of the "queue"
1879  */
Term_key_push(int k)1880 errr Term_key_push(int k)
1881 {
1882 	/* Hack -- Refuse to enqueue non-keys */
1883 	if (!k) return (-1);
1884 
1885 	/* Hack -- Overflow may induce circular queue */
1886 	if (Term->key_tail == 0) Term->key_tail = Term->key_size;
1887 
1888 	/* Back up, Store the char */
1889 	Term->key_queue[--Term->key_tail] = k;
1890 
1891 	/* Success (unless overflow) */
1892 	if (Term->key_head != Term->key_tail) return (0);
1893 
1894 #if 0
1895 	/* Hack -- Forget the oldest key */
1896 	if (++Term->key_tail == Term->key_size) Term->key_tail = 0;
1897 #endif
1898 
1899 	/* Problem */
1900 	return (1);
1901 }
1902 
1903 
1904 
1905 
1906 
1907 /*
1908  * Check for a pending keypress on the key queue.
1909  *
1910  * Store the keypress, if any, in "ch", and return "0".
1911  * Otherwise store "zero" in "ch", and return "1".
1912  *
1913  * Wait for a keypress if "wait" is true.
1914  *
1915  * Remove the keypress if "take" is true.
1916  */
Term_inkey(char * ch,bool wait,bool take)1917 errr Term_inkey(char *ch, bool wait, bool take)
1918 {
1919 	/* Assume no key */
1920 	(*ch) = '\0';
1921 
1922 	/* Hack -- get bored */
1923 	if (!Term->never_bored)
1924 	{
1925 		/* Process random events */
1926 		Term_xtra(TERM_XTRA_BORED, 0);
1927 	}
1928 
1929 	/* Wait */
1930 	if (wait)
1931 	{
1932 		/* Process pending events while necessary */
1933 		while (Term->key_head == Term->key_tail)
1934 		{
1935 			/* Process events (wait for one) */
1936 			Term_xtra(TERM_XTRA_EVENT, TRUE);
1937 		}
1938 	}
1939 
1940 	/* Do not Wait */
1941 	else
1942 	{
1943 		/* Process pending events if necessary */
1944 		if (Term->key_head == Term->key_tail)
1945 		{
1946 			/* Process events (do not wait) */
1947 			Term_xtra(TERM_XTRA_EVENT, FALSE);
1948 		}
1949 	}
1950 
1951 	/* No keys are ready */
1952 	if (Term->key_head == Term->key_tail) return (1);
1953 
1954 	/* Extract the next keypress */
1955 	(*ch) = Term->key_queue[Term->key_tail];
1956 
1957 	/* If requested, advance the queue, wrap around if necessary */
1958 	if (take && (++Term->key_tail == Term->key_size)) Term->key_tail = 0;
1959 
1960 	/* Success */
1961 	return (0);
1962 }
1963 
1964 
1965 
1966 /*** Extra routines ***/
1967 
1968 
1969 /*
1970  * Save the "requested" screen into "memorized" screen list
1971  *
1972  * Every "Term_save()" should match exactly one "Term_load()"
1973  */
Term_save(void)1974 void Term_save(void)
1975 {
1976 	int w = Term->wid;
1977 	int h = Term->hgt;
1978 
1979 	term_win *tmp;
1980 
1981 	/* Allocate window */
1982 	MAKE(tmp, term_win);
1983 
1984 	/* Initialize window */
1985 	(void)term_win_init(tmp, w, h);
1986 
1987 	/* Grab */
1988 	(void)term_win_copy(tmp, Term->scr, w, h);
1989 
1990 	/* Add the front of the list */
1991 	tmp->next = Term->scr;
1992 	Term->scr = tmp;
1993 
1994 	/* Wipe bigtile regions as required */
1995 	Term->scr->wipe_bigtile = TRUE;
1996 }
1997 
1998 
1999 /*
2000  * Restore the "requested" contents (see above).
2001  *
2002  * Every "Term_save()" should match exactly one "Term_load()"
2003  */
Term_load(void)2004 void Term_load(void)
2005 {
2006 	int y;
2007 
2008 	int w = Term->wid;
2009 	int h = Term->hgt;
2010 
2011 	term_win *tmp;
2012 
2013 	/* Pop off window from the list */
2014 	if (Term->scr->next)
2015 	{
2016 		/* Hack - is bigtile not on? */
2017 		if ((Term->scr->big_x1 == -1) && (Term->scr->next->big_x1 != -1))
2018 		{
2019 			/* Erase term so boundaries are cleared properly. */
2020 			Term_clear();
2021 			Term_redraw();
2022 		}
2023 
2024 		/* Save pointer to old window */
2025 		tmp = Term->scr;
2026 
2027 		/* Point to new window */
2028 		Term->scr = Term->scr->next;
2029 
2030 		/* Free the old window */
2031 		(void)term_win_nuke(tmp);
2032 
2033 		/* Kill */
2034 		KILL(tmp);
2035 	}
2036 
2037 	/* Assume change */
2038 	for (y = 0; y < h; y++)
2039 	{
2040 		/* Assume change */
2041 		Term->x1[y] = 0;
2042 		Term->x2[y] = w - 1;
2043 	}
2044 
2045 	/* Assume change */
2046 	Term->y1 = 0;
2047 	Term->y2 = h - 1;
2048 
2049 	/* Hack - is bigtile on? */
2050 	if (Term->scr->big_x1 != -1)
2051 	{
2052 		/* Redraw term */
2053 		Term_redraw();
2054 	}
2055 }
2056 
2057 
2058 /*
2059  * React to a new physical window size.
2060  */
Term_resize(int w,int h)2061 errr Term_resize(int w, int h)
2062 {
2063 	int i;
2064 
2065 	int wid, hgt;
2066 
2067 	byte *hold_x1;
2068 	byte *hold_x2;
2069 
2070 	term_win *scr;
2071 
2072 	/* Resizing is forbidden */
2073 	if (Term->fixed_shape) return (-1);
2074 
2075 	/* Ignore illegal changes */
2076 	if ((w < 1) || (h < 1)) return (-1);
2077 
2078 
2079 	/* Ignore non-changes */
2080 	if ((Term->wid == w) && (Term->hgt == h)) return (1);
2081 
2082 
2083 	/* Minimum dimensions */
2084 	wid = MIN(Term->wid, w);
2085 	hgt = MIN(Term->hgt, h);
2086 
2087 	/* Resize old window */
2088 	(void) Term_resize_win(w, h, wid, hgt, Term->old);
2089 
2090 	/* Resize current window and the previous list */
2091 	for (scr = Term->scr; scr; scr = scr->next)
2092 	{
2093 		Term_resize_win(w, h, wid, hgt, scr);
2094 	}
2095 
2096 	/* Save scanners */
2097 	hold_x1 = Term->x1;
2098 	hold_x2 = Term->x2;
2099 
2100 	/* Create new scanners */
2101 	C_MAKE(Term->x1, h, byte);
2102 	C_MAKE(Term->x2, h, byte);
2103 
2104 	/* Free some arrays */
2105 	KILL(hold_x1);
2106 	KILL(hold_x2);
2107 
2108 	/* Save new size */
2109 	Term->wid = w;
2110 	Term->hgt = h;
2111 
2112 	/* Force "total erase" */
2113 	Term->total_erase = TRUE;
2114 
2115 	/* Assume change */
2116 	for (i = 0; i < h; i++)
2117 	{
2118 		/* Assume change */
2119 		Term->x1[i] = 0;
2120 		Term->x2[i] = w - 1;
2121 	}
2122 
2123 	/* Assume change */
2124 	Term->y1 = 0;
2125 	Term->y2 = h - 1;
2126 
2127 	/* Execute the "resize_hook" hook, if available */
2128 	if (Term->resize_hook)
2129 	{
2130 		Term->resize_hook();
2131 	}
2132 
2133 	/* Success */
2134 	return (0);
2135 }
2136 
2137 
2138 
2139 /*
2140  * Activate a new Term (and deactivate the current Term)
2141  *
2142  * This function is extremely important, and also somewhat bizarre.
2143  * It is the only function that should "modify" the value of "Term".
2144  *
2145  * To "create" a valid "term", one should do "term_init(t)", then
2146  * set the various flags and hooks, and then do "Term_activate(t)".
2147  */
Term_activate(term * t)2148 void Term_activate(term *t)
2149 {
2150 	/* Hack -- already done */
2151 	if (Term == t) return;
2152 
2153 	/* Deactivate the old Term */
2154 	if (Term) Term_xtra(TERM_XTRA_LEVEL, 0);
2155 
2156 	/* Hack -- Call the special "init" hook */
2157 	if (t && !t->active_flag)
2158 	{
2159 		/* Call the "init" hook */
2160 		if (t->init_hook) (*t->init_hook) (t);
2161 
2162 		/* Remember */
2163 		t->active_flag = TRUE;
2164 
2165 		/* Assume mapped */
2166 		t->mapped_flag = TRUE;
2167 	}
2168 
2169 	/* Remember the Term */
2170 	Term = t;
2171 
2172 	/* Activate the new Term */
2173 	if (Term) Term_xtra(TERM_XTRA_LEVEL, 1);
2174 }
2175 
2176 
2177 
2178 /*
2179  * Nuke a term
2180  */
term_nuke(term * t)2181 errr term_nuke(term *t)
2182 {
2183 	term_win *tmp, *tmp2;
2184 
2185 	/* Hack -- Call the special "nuke" hook */
2186 	if (t->active_flag)
2187 	{
2188 		/* Call the "nuke" hook */
2189 		if (t->nuke_hook) (*t->nuke_hook) (t);
2190 
2191 		/* Remember */
2192 		t->active_flag = FALSE;
2193 
2194 		/* Assume not mapped */
2195 		t->mapped_flag = FALSE;
2196 	}
2197 
2198 
2199 	/* Nuke "displayed" */
2200 	(void)term_win_nuke(t->old);
2201 
2202 	/* Kill "displayed" */
2203 	KILL(t->old);
2204 
2205 	for (tmp = t->scr; tmp; tmp = tmp2)
2206 	{
2207 		tmp2 = tmp->next;
2208 
2209 		/* Nuke "requested" */
2210 		(void) term_win_nuke(tmp);
2211 
2212 		/* Kill "requested" */
2213 		KILL(tmp);
2214 	}
2215 
2216 	/* Free some arrays */
2217 	KILL(t->x1);
2218 	KILL(t->x2);
2219 
2220 	/* Free the input queue */
2221 	KILL(t->key_queue);
2222 
2223 	/* Success */
2224 	return (0);
2225 }
2226 
2227 
2228 /*
2229  * Initialize a term, using a window of the given size.
2230  * Also prepare the "input queue" for "k" keypresses
2231  * By default, the cursor starts out "invisible"
2232  * By default, we "erase" using "black spaces"
2233  */
term_init(term * t,int w,int h,int k)2234 errr term_init(term *t, int w, int h, int k)
2235 {
2236 	int y;
2237 
2238 	/* Wipe it */
2239 	(void)WIPE(t, term);
2240 
2241 
2242 	/* Prepare the input queue */
2243 	t->key_head = t->key_tail = 0;
2244 
2245 	/* Determine the input queue size */
2246 	t->key_size = k;
2247 
2248 	/* Allocate the input queue */
2249 	C_MAKE(t->key_queue, t->key_size, char);
2250 
2251 
2252 	/* Save the size */
2253 	t->wid = w;
2254 	t->hgt = h;
2255 
2256 	/* Allocate change arrays */
2257 	C_MAKE(t->x1, h, byte);
2258 	C_MAKE(t->x2, h, byte);
2259 
2260 
2261 	/* Allocate "displayed" */
2262 	MAKE(t->old, term_win);
2263 
2264 	/* Initialize "displayed" */
2265 	(void)term_win_init(t->old, w, h);
2266 
2267 
2268 	/* Allocate "requested" */
2269 	MAKE(t->scr, term_win);
2270 
2271 	/* Initialize "requested" */
2272 	(void)term_win_init(t->scr, w, h);
2273 
2274 
2275 	/* Assume change */
2276 	for (y = 0; y < h; y++)
2277 	{
2278 		/* Assume change */
2279 		t->x1[y] = 0;
2280 		t->x2[y] = w - 1;
2281 	}
2282 
2283 	/* Assume change */
2284 	t->y1 = 0;
2285 	t->y2 = h - 1;
2286 
2287 	/* Force "total erase" */
2288 	t->total_erase = TRUE;
2289 
2290 
2291 	/* Default "blank" */
2292 	t->attr_blank = 0;
2293 	t->char_blank = ' ';
2294 
2295 
2296 	/* Success */
2297 	return (0);
2298 }
2299 
2300 
2301 /*
2302  * Make a region of a term 'bigtiled'
2303  */
Term_bigregion(int x1,int y1,int y2)2304 errr Term_bigregion(int x1, int y1, int y2)
2305 {
2306     /* Save region */
2307 	Term->scr->big_x1 = x1;
2308 	Term->scr->big_y1 = y1;
2309 	Term->scr->big_y2 = y2;
2310 
2311     /* Success */
2312 	return (0);
2313 }
2314