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