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