4 * Copyright (c) 1997 Ben Harrison
6 * This software may be copied and distributed for educational, research,
7 * and not for profit purposes provided that this copyright and statement
8 * are included in all such copies.
11 /* Purpose: a generic, efficient, terminal window package -BEN- */
18 /* Special flags in the attr data */
19 #define AF_BIGTILE2 0xf0
23 #define AF_KANJI1 0x10
24 #define AF_KANJI2 0x20
25 #define AF_KANJIC 0x0f
28 * 属性に全角文字の1バイト目、2バイト目も記憶。
33 * This file provides a generic, efficient, terminal window package,
34 * which can be used not only on standard terminal environments such
35 * as dumb terminals connected to a Unix box, but also in more modern
36 * "graphic" environments, such as the Macintosh or Unix/X11.
38 * Each "window" works like a standard "dumb terminal", that is, it
39 * can display a two dimensional array of grids containing colored
40 * textual symbols, plus an optional cursor, and it can be used to
41 * get keypress events from the user.
43 * In fact, this package can simply be used, if desired, to support
44 * programs which will look the same on a dumb terminal as they do
45 * on a graphic platform such as the Macintosh.
47 * This package was designed to help port the game "Angband" to a wide
48 * variety of different platforms. Angband, like many other games in
49 * the "rogue-like" heirarchy, requires, at the minimum, the ability
50 * to display "colored textual symbols" in a standard 80x24 "window",
51 * such as that provided by most dumb terminals, and many old personal
52 * computers, and to check for "keypresses" from the user. The major
53 * concerns were thus portability and efficiency, so Angband could be
54 * easily ported to many different systems, with minimal effort, and
55 * yet would run quickly on each of these sys
56 tems, no matter what kind
57 * of underlying hardware/software support was being used.
59 * It is important to understand the differences between the older
60 * "dumb terminals" and the newer "graphic interface" machines, since
61 * this package was designed to work with both types of systems.
64 * waiting for a keypress is complex
65 * checking for a keypress is often cheap
66 * changing "colors" may be expensive
67 * the "color" of a "blank" is rarely important
68 * moving the "cursor" is relatively cheap
69 * use a "software" cursor (only moves when requested)
70 * drawing characters normally will not erase old ones
71 * drawing a character on the cursor often erases it
72 * may have fast routines for "clear a region"
73 * the bottom right corner is usually not special
76 * waiting for a keypress is simple
77 * checking for a keypress is often expensive
78 * changing "colors" is usually cheap
79 * the "color" of a "blank" may be important
80 * moving the "cursor" may be expensive
81 * use a "hardware" cursor (moves during screen updates)
82 * drawing new symbols automatically erases old ones
83 * characters may only be drawn at the cursor location
84 * drawing a character on the cursor will move the cursor
85 * may have fast routines for "clear entire window"
86 * may have fast routines for "clear to end of line"
87 * the bottom right corner is often dangerous
90 * This package provides support for multiple windows, each of an
91 * arbitrary size (up to 255x255), each with its own set of flags,
92 * and its own hooks to handle several low-level procedures which
93 * differ from platform to platform. Then the main program simply
94 * creates one or more "term" structures, setting the various flags
95 * and hooks in a manner appropriate for the current platform, and
96 * then it can use the various "term" structures without worrying
97 * about the underlying platform.
100 * This package allows each "grid" in each window to hold an attr/char
101 * pair, with each ranging from 0 to 255, and makes very few assumptions
102 * about the meaning of any attr/char values. Normally, we assume that
103 * "attr 0" is "black", with the semantics that "black" text should be
104 * sent to "Term_wipe()" instead of "Term_text()", but this sematics is
105 * modified if either the "always_pict" or the "always_text" flags are
106 * set. We assume that "char 0" is "dangerous", since placing such a
107 * "char" in the middle of a string "terminates" the string, and usually
108 * we prevent its use.
110 * Finally, we use a special attr/char pair, defaulting to "attr 0" and
111 * "char 32", also known as "black space", when we "erase" or "clear"
112 * any window, but this pair can be redefined to any pair, including
113 * the standard "white space", or the bizarre "emptiness" ("attr 0"
114 * and "char 0"), as long as various obscure restrictions are met.
117 * This package provides several functions which allow a program to
118 * interact with the "term" structures. Most of the functions allow
119 * the program to "request" certain changes to the current "term",
120 * such as moving the cursor, drawing an attr/char pair, erasing a
121 * region of grids, hiding the cursor, etc. Then there is a special
122 * function which causes all of the "pending" requests to be performed
123 * in an efficient manner. There is another set of functions which
124 * allow the program to query the "requested state" of the current
125 * "term", such as asking for the cursor location, or what attr/char
126 * is at a given location, etc. There is another set of functions
127 * dealing with "keypress" events, which allows the program to ask if
128 * the user has pressed any keys, or to forget any keys the user pressed.
129 * There is a pair of functions to allow this package to memorize the
130 * contents of the current "term", and to restore these contents at
131 * a later time. There is a special function which allows the program
132 * to specify which "term" structure should be the "current" one. At
133 * the lowest level, there is a set of functions which allow a new
134 * "term" to be initialized or destroyed, and which allow this package,
135 * or a program, to access the special "hooks" defined for the current
136 * "term", and a set of functions which those "hooks" can use to inform
137 * this package of the results of certain occurances, for example, one
138 * such function allows this package to learn about user keypresses,
139 * detected by one of the special "hooks".
141 * We provide, among other things, the functions "Term_keypress()"
142 * to "react" to keypress events, and "Term_redraw()" to redraw the
143 * entire window, plus "Term_resize()" to note a new size.
146 * Note that the current "term" contains two "window images". One of
147 * these images represents the "requested" contents of the "term", and
148 * the other represents the "actual" contents of the "term", at the time
149 * of the last performance of pending requests. This package uses these
150 * two images to determine the "minimal" amount of work needed to make
151 * the "actual" contents of the "term" match the "requested" contents of
152 * the "term". This method is not perfect, but it often reduces the
153 * amount of work needed to perform the pending requests, which thus
154 * increases the speed of the program itself. This package promises
155 * that the requested changes will appear to occur either "all at once"
156 * or in a "top to bottom" order. In addition, a "cursor" is maintained,
157 * and this cursor is updated along with the actual window contents.
159 * Currently, the "Term_fresh()" routine attempts to perform the "minimum"
160 * number of physical updates, in terms of total "work" done by the hooks
161 * Term_wipe(), Term_text(), and Term_pict(), making use of the fact that
162 * adjacent characters of the same color can both be drawn together using
163 * the "Term_text()" hook, and that "black" text can often be sent to the
164 * "Term_wipe()" hook instead of the "Term_text()" hook, and if something
165 * is already displayed in a window, then it is not necessary to display
166 * it again. Unfortunately, this may induce slightly non-optimal results
167 * in some cases, in particular, those in which, say, a string of ten
168 * characters needs to be written, but the fifth character has already
169 * been displayed. Currently, this will cause the "Term_text()" routine
170 * to be called once for each half of the string, instead of once for the
171 * whole string, which, on some machines, may be non-optimal behavior.
173 * The new formalism includes a "displayed" screen image (old) which
174 * is actually seen by the user, a "requested" screen image (scr)
175 * which is being prepared for display, a "memorized" screen image
176 * (mem) which is used to save and restore screen images, and a
177 * "temporary" screen image (tmp) which is currently unused.
180 * Several "flags" are available in each "term" to allow the underlying
181 * visual system (which initializes the "term" structure) to "optimize"
182 * the performance of this package for the given system, or to request
183 * certain behavior which is helpful/required for the given system.
185 * The "soft_cursor" flag indicates the use of a "soft" cursor, which
186 * only moves when explicitly requested,and which is "erased" when
187 * any characters are drawn on top of it. This flag is used for all
188 * "graphic" systems which handle the cursor by "drawing" it.
190 * The "icky_corner" flag indicates that the bottom right "corner"
191 * of the windows are "icky", and "printing" anything there may
192 * induce "messy" behavior, such as "scrolling". This flag is used
193 * for most old "dumb terminal" systems.
196 * The "term" structure contains the following function "hooks":
198 * Term->init_hook = Init the term
199 * Term->nuke_hook = Nuke the term
200 * Term->user_hook = Perform user actions
201 * Term->xtra_hook = Perform extra actions
202 * Term->curs_hook = Draw (or Move) the cursor
203 * Term->bigcurs_hook = Draw (or Move) the big cursor (bigtile mode)
204 * Term->wipe_hook = Draw some blank spaces
205 * Term->text_hook = Draw some text in the window
206 * Term->pict_hook = Draw some attr/chars in the window
208 * The "Term->user_hook" hook provides a simple hook to an implementation
209 * defined function, with application defined semantics. It is available
210 * to the program via the "Term_user()" function.
212 * The "Term->xtra_hook" hook provides a variety of different functions,
213 * based on the first parameter (which should be taken from the various
214 * TERM_XTRA_* defines) and the second parameter (which may make sense
215 * only for some first parameters). It is available to the program via
216 * the "Term_xtra()" function, though some first parameters are only
217 * "legal" when called from inside this package.
219 * The "Term->curs_hook" hook provides this package with a simple way
220 * to "move" or "draw" the cursor to the grid "x,y", depending on the
221 * setting of the "soft_cursor" flag. Note that the cursor is never
222 * redrawn if "nothing" has happened to the screen (even temporarily).
223 * This hook is required.
225 * The "Term->wipe_hook" hook provides this package with a simple way
226 * to "erase", starting at "x,y", the next "n" grids. This hook assumes
227 * that the input is valid. This hook is required, unless the setting
228 * of the "always_pict" or "always_text" flags makes it optional.
230 * The "Term->text_hook" hook provides this package with a simple way
231 * to "draw", starting at "x,y", the "n" chars contained in "cp", using
232 * the attr "a". This hook assumes that the input is valid, and that
233 * "n" is between 1 and 256 inclusive, but it should NOT assume that
234 * the contents of "cp" are null-terminated. This hook is required,
235 * unless the setting of the "always_pict" flag makes it optional.
237 * The "Term->pict_hook" hook provides this package with a simple way
238 * to "draw", starting at "x,y", the "n" attr/char pairs contained in
239 * the arrays "ap" and "cp". This hook assumes that the input is valid,
240 * and that "n" is between 1 and 256 inclusive, but it should NOT assume
241 * that the contents of "cp" are null-terminated. This hook is optional,
242 * unless the setting of the "always_pict" or "higher_pict" flags make
243 * it required. Note that recently, this hook was changed from taking
244 * a byte "a" and a char "c" to taking a length "n", an array of bytes
245 * "ap" and an array of chars "cp". Old implementations of this hook
246 * should now iterate over all "n" attr/char pairs.
249 * The game "Angband" uses a set of files called "main-xxx.c", for
250 * various "xxx" suffixes. Most of these contain a function called
251 * "init_xxx()", that will prepare the underlying visual system for
252 * use with Angband, and then create one or more "term" structures,
253 * using flags and hooks appropriate to the given platform, so that
254 * the "main()" function can call one (or more) of the "init_xxx()"
255 * functions, as appropriate, to prepare the required "term" structs
256 * (one for each desired sub-window), and these "init_xxx()" functions
257 * are called from a centralized "main()" function in "main.c". Other
258 * "main-xxx.c" systems contain their own "main()" function which, in
259 * addition to doing everything needed to initialize the actual program,
260 * also does everything that the normal "init_xxx()" functions would do.
262 * The game "Angband" defines, in addition to "attr 0", all of the
263 * attr codes from 1 to 15, using definitions in "defines.h", and
264 * thus the "main-xxx.c" files used by Angband must handle these
265 * attr values correctly. Also, they must handle all other attr
266 * values, though they may do so in any way they wish, for example,
267 * by always taking every attr code mod 16. Many of the "main-xxx.c"
268 * files use "white space" ("attr 1" / "char 32") to "erase" or "clear"
269 * any window, for efficiency.
271 * The game "Angband" uses the "Term_user" hook to allow any of the
272 * "main-xxx.c" files to interact with the user, by calling this hook
273 * whenever the user presses the "!" key when the game is waiting for
274 * a new command. This could be used, for example, to provide "unix
275 * shell commands" to the Unix versions of the game.
277 * See "main-xxx.c" for a simple skeleton file which can be used to
278 * create a "visual system" for a new platform when porting Angband.
294 /*** Local routines ***/
298 * Nuke a term_win (see below)
300 static errr term_win_nuke(term_win *s, TERM_LEN w, TERM_LEN h)
302 /* Free the window access arrays */
303 C_KILL(s->a, h, TERM_COLOR*);
304 C_KILL(s->c, h, char*);
306 /* Free the window content arrays */
307 C_KILL(s->va, h * w, TERM_COLOR);
308 C_KILL(s->vc, h * w, char);
310 /* Free the terrain access arrays */
311 C_KILL(s->ta, h, TERM_COLOR*);
312 C_KILL(s->tc, h, char*);
314 /* Free the terrain content arrays */
315 C_KILL(s->vta, h * w, TERM_COLOR);
316 C_KILL(s->vtc, h * w, char);
324 * Initialize a "term_win" (using the given window size)
326 static errr term_win_init(term_win *s, TERM_LEN w, TERM_LEN h)
330 /* Make the window access arrays */
331 C_MAKE(s->a, h, TERM_COLOR*);
332 C_MAKE(s->c, h, char*);
334 /* Make the window content arrays */
335 C_MAKE(s->va, h * w, TERM_COLOR);
336 C_MAKE(s->vc, h * w, char);
338 /* Make the terrain access arrays */
339 C_MAKE(s->ta, h, TERM_COLOR*);
340 C_MAKE(s->tc, h, char*);
342 /* Make the terrain content arrays */
343 C_MAKE(s->vta, h * w, TERM_COLOR);
344 C_MAKE(s->vtc, h * w, char);
347 /* Prepare the window access arrays */
348 for (y = 0; y < h; y++)
350 s->a[y] = s->va + w * y;
351 s->c[y] = s->vc + w * y;
353 s->ta[y] = s->vta + w * y;
354 s->tc[y] = s->vtc + w * y;
363 * Copy a "term_win" from another
365 static errr term_win_copy(term_win *s, term_win *f, TERM_LEN w, TERM_LEN h)
370 for (y = 0; y < h; y++)
372 TERM_COLOR *f_aa = f->a[y];
373 char *f_cc = f->c[y];
375 TERM_COLOR *s_aa = s->a[y];
376 char *s_cc = s->c[y];
378 TERM_COLOR *f_taa = f->ta[y];
379 char *f_tcc = f->tc[y];
381 TERM_COLOR *s_taa = s->ta[y];
382 char *s_tcc = s->tc[y];
384 for (x = 0; x < w; x++)
406 /*** External hooks ***/
410 * Execute the "Term->user_hook" hook, if available (see above).
412 errr Term_user(int n)
414 /* Verify the hook */
415 if (!Term->user_hook) return (-1);
418 return ((*Term->user_hook)(n));
422 * Execute the "Term->xtra_hook" hook, if available (see above).
424 errr Term_xtra(int n, int v)
426 /* Verify the hook */
427 if (!Term->xtra_hook) return (-1);
430 return ((*Term->xtra_hook)(n, v));
439 * Hack -- fake hook for "Term_curs()" (see above)
441 static errr Term_curs_hack(TERM_LEN x, TERM_LEN y)
451 * Hack -- fake hook for "Term_bigcurs()" (see above)
453 static errr Term_bigcurs_hack(TERM_LEN x, TERM_LEN y)
455 return (*Term->curs_hook)(x, y);
459 * Hack -- fake hook for "Term_wipe()" (see above)
461 static errr Term_wipe_hack(TERM_LEN x, TERM_LEN y, int n)
472 * Hack -- fake hook for "Term_text()" (see above)
474 static errr Term_text_hack(TERM_LEN x, TERM_LEN y, int n, TERM_COLOR a, concptr cp)
487 * Hack -- fake hook for "Term_pict()" (see above)
489 static errr Term_pict_hack(TERM_LEN x, TERM_LEN y, int n, const TERM_COLOR *ap, concptr cp, const TERM_COLOR *tap, concptr tcp)
505 /*** Efficient routines ***/
509 * Mentally draw an attr/char at a given location
510 * Assumes given location and values are valid.
512 void Term_queue_char(TERM_LEN x, TERM_LEN y, TERM_COLOR a, char c, TERM_COLOR ta, char tc)
514 term_win *scrn = Term->scr;
516 TERM_COLOR *scr_aa = &scrn->a[y][x];
517 char *scr_cc = &scrn->c[y][x];
519 TERM_COLOR *scr_taa = &scrn->ta[y][x];
520 char *scr_tcc = &scrn->tc[y][x];
522 /* Hack -- Ignore non-changes */
523 if ((*scr_aa == a) && (*scr_cc == c) &&
524 (*scr_taa == ta) && (*scr_tcc == tc)) return;
526 /* Save the "literal" information */
533 /* Check for new min/max row info */
534 if (y < Term->y1) Term->y1 = (byte_hack)y;
535 if (y > Term->y2) Term->y2 = (byte_hack)y;
537 /* Check for new min/max col info for this row */
538 if (x < Term->x1[y]) Term->x1[y] = (byte_hack)x;
539 if (x > Term->x2[y]) Term->x2[y] = (byte_hack)x;
542 if (((scrn->a[y][x] & AF_BIGTILE2) == AF_BIGTILE2) ||
543 (scrn->a[y][x] & AF_KANJI2))
545 if ((scrn->a[y][x] & AF_BIGTILE2) == AF_BIGTILE2)
547 if ((x - 1) < Term->x1[y]) Term->x1[y]--;
552 * Bigtile version of Term_queue_char().
553 * If use_bigtile is FALSE, simply call Term_queue_char().
554 * Otherwise, mentally draw a pair of attr/char at a given location.
555 * Assumes given location and values are valid.
557 void Term_queue_bigchar(TERM_LEN x, TERM_LEN y, TERM_COLOR a, char c, TERM_COLOR ta, char tc)
562 * A table which relates each ascii character to a multibyte
565 * 「■」は二倍幅豆腐の内部コードに使用。
567 static char ascii_to_zenkaku[] =
579 /* If non bigtile mode, call orginal function */
582 Term_queue_char(x, y, a, c, ta, tc);
586 /* A tile becomes a Bigtile */
587 if ((a & AF_TILE1) && (c & 0x80))
589 /* Mark it as a Bigtile */
594 /* Ignore non-tile background */
595 if (!((ta & AF_TILE1) && (tc & 0x80)))
604 * Use a multibyte character instead of a dirty pair of ASCII
607 else if (' ' <= c) /* isprint(c) */
609 c2 = ascii_to_zenkaku[2 * (c - ' ') + 1];
610 c = ascii_to_zenkaku[2 * (c - ' ')];
612 /* Mark it as a Kanji */
620 /* Dirty pair of ASCII characters */
625 /* Display pair of attr/char */
626 Term_queue_char(x, y, a, c, ta, tc);
627 Term_queue_char(x + 1, y, a2, c2, 0, 0);
632 * Mentally draw a string of attr/chars at a given location
634 * Assumes given location and values are valid.
636 * This function is designed to be fast, with no consistancy checking.
637 * It is used to update the map in the game.
639 void Term_queue_line(TERM_LEN x, TERM_LEN y, int n, TERM_COLOR *a, char *c, TERM_COLOR *ta, char *tc)
641 term_win *scrn = Term->scr;
646 TERM_COLOR *scr_aa = &scrn->a[y][x];
647 char *scr_cc = &scrn->c[y][x];
649 TERM_COLOR *scr_taa = &scrn->ta[y][x];
650 char *scr_tcc = &scrn->tc[y][x];
654 /* Hack -- Ignore non-changes */
655 if ((*scr_aa == *a) && (*scr_cc == *c) &&
656 (*scr_taa == *ta) && (*scr_tcc == *tc))
670 /* Save the "literal" information */
674 /* Save the "literal" information */
678 /* Track minimum changed column */
681 /* Track maximum changed column */
687 /* Expand the "change area" as needed */
690 /* Check for new min/max row info */
691 if (y < Term->y1) Term->y1 = (byte_hack)y;
692 if (y > Term->y2) Term->y2 = (byte_hack)y;
694 /* Check for new min/max col info in this row */
695 if (x1 < Term->x1[y]) Term->x1[y] = (byte_hack)x1;
696 if (x2 > Term->x2[y]) Term->x2[y] = (byte_hack)x2;
703 * Mentally draw some attr/chars at a given location
705 * Assumes that (x,y) is a valid location, that the first "n" characters
706 * of the string "s" are all valid (non-zero), and that (x+n-1,y) is also
707 * a valid location, so the first "n" characters of "s" can all be added
708 * starting at (x,y) without causing any illegal operations.
710 void Term_queue_chars(TERM_LEN x, TERM_LEN y, int n, TERM_COLOR a, concptr s)
712 TERM_LEN x1 = -1, x2 = -1;
714 TERM_COLOR *scr_aa = Term->scr->a[y];
716 char *scr_cc = Term->scr->c[y];
718 TERM_COLOR *scr_taa = Term->scr->ta[y];
719 char *scr_tcc = Term->scr->tc[y];
721 char *scr_cc = Term->scr->c[y];
723 TERM_COLOR *scr_taa = Term->scr->ta[y];
724 char *scr_tcc = Term->scr->tc[y];
730 if (n == 0 || *s == 0) return;
732 * 全角文字の右半分から文字を表示する場合、
736 if ((scr_aa[x] & AF_KANJI2) && (scr_aa[x] & AF_BIGTILE2) != AF_BIGTILE2)
739 scr_aa[x - 1] &= AF_KANJIC;
743 /* Queue the attr/chars */
744 for ( ; n; x++, s++, n--)
747 /* 特殊文字としてMSBが立っている可能性がある */
748 /* その場合attrのMSBも立っているのでこれで識別する */
750 if (!(a & AF_TILE1) && iskanji(*s))
755 byte na1 = (a | AF_KANJI1);
756 byte na2 = (a | AF_KANJI2);
758 if((--n == 0) || !nc2) break;
760 if(scr_aa[x++] == na1 && scr_aa[x] == na2 &&
761 scr_cc[x - 1] == nc1 && scr_cc[x] == nc2 &&
762 (scr_taa[x - 1] == 0) && (scr_taa[x]==0) &&
763 (scr_tcc[x - 1] == 0) && (scr_tcc[x]==0) )
771 if(x1 < 0) x1 = x - 1;
777 TERM_COLOR oa = scr_aa[x];
780 TERM_COLOR ota = scr_taa[x];
781 char otc = scr_tcc[x];
783 /* Hack -- Ignore non-changes */
784 if ((oa == a) && (oc == *s) && (ota == 0) && (otc == 0)) continue;
786 /* Save the "literal" information */
793 /* Note the "range" of window updates */
803 * 全角文字の左半分で表示を終了する場合、
805 * (条件追加:タイルの1文字目でない事を確かめるように。)
810 Term_get_size(&w, &h);
811 if (x != w && !(scr_aa[x] & AF_TILE1) && (scr_aa[x] & AF_KANJI2))
814 scr_aa[x] &= AF_KANJIC;
820 /* Expand the "change area" as needed */
823 /* Check for new min/max row info */
824 if (y < Term->y1) Term->y1 = (byte_hack)y;
825 if (y > Term->y2) Term->y2 = (byte_hack)y;
827 /* Check for new min/max col info in this row */
828 if (x1 < Term->x1[y]) Term->x1[y] = (byte_hack)x1;
829 if (x2 > Term->x2[y]) Term->x2[y] = (byte_hack)x2;
835 /*** Refresh routines ***/
839 * Flush a row of the current window (see "Term_fresh")
841 * Display text using "Term_pict()"
843 static void Term_fresh_row_pict(TERM_LEN y, TERM_LEN x1, TERM_LEN x2)
847 TERM_COLOR *old_aa = Term->old->a[y];
848 char *old_cc = Term->old->c[y];
850 TERM_COLOR *scr_aa = Term->scr->a[y];
851 char *scr_cc = Term->scr->c[y];
853 TERM_COLOR *old_taa = Term->old->ta[y];
854 char *old_tcc = Term->old->tc[y];
856 TERM_COLOR *scr_taa = Term->scr->ta[y];
857 char *scr_tcc = Term->scr->tc[y];
882 /* Scan "modified" columns */
883 for (x = x1; x <= x2; x++)
885 /* See what is currently here */
889 /* See what is desired there */
903 /* 特殊文字としてMSBが立っている可能性がある */
904 /* その場合attrのMSBも立っているのでこれで識別する */
906 kanji = (iskanji(nc) && !(na & AF_TILE1));
915 /* Handle unchanged grids */
917 if ((na == oa) && (nc == oc) && (nta == ota) && (ntc == otc)
918 &&(!kanji || (scr_aa[x + 1] == old_aa[x + 1] &&
919 scr_cc[x + 1] == old_cc[x + 1] &&
920 scr_taa[x + 1] == old_taa[x + 1] &&
921 scr_tcc[x + 1] == old_tcc[x + 1])))
923 if ((na == oa) && (nc == oc) && (nta == ota) && (ntc == otc))
929 /* Draw pending attr/char pairs */
930 (void)((*Term->pict_hook)(fx, y, fn,
931 &scr_aa[fx], &scr_cc[fx], &scr_taa[fx], &scr_tcc[fx]));
949 /* Save new contents */
956 /* Restart and Advance */
957 if (fn++ == 0) fx = x;
963 /* Draw pending attr/char pairs */
964 (void)((*Term->pict_hook)(fx, y, fn,
965 &scr_aa[fx], &scr_cc[fx], &scr_taa[fx], &scr_tcc[fx]));
972 * Flush a row of the current window (see "Term_fresh")
974 * Display text using "Term_text()" and "Term_wipe()",
975 * but use "Term_pict()" for high-bit attr/char pairs
977 static void Term_fresh_row_both(TERM_LEN y, int x1, int x2)
981 TERM_COLOR *old_aa = Term->old->a[y];
982 char *old_cc = Term->old->c[y];
984 TERM_COLOR *scr_aa = Term->scr->a[y];
985 char *scr_cc = Term->scr->c[y];
987 TERM_COLOR *old_taa = Term->old->ta[y];
988 char *old_tcc = Term->old->tc[y];
989 TERM_COLOR *scr_taa = Term->scr->ta[y];
990 char *scr_tcc = Term->scr->tc[y];
997 /* The "always_text" flag */
998 int always_text = Term->always_text;
1000 /* Pending length */
1007 byte fa = Term->attr_blank;
1016 /* 全角文字の2バイト目かどうか */
1019 /* Scan "modified" columns */
1020 for (x = x1; x <= x2; x++)
1022 /* See what is currently here */
1026 /* See what is desired there */
1040 /* 特殊文字としてMSBが立っている可能性がある */
1041 /* その場合attrのMSBも立っているのでこれで識別する */
1043 /* kanji = (iskanji(nc)); */
1044 kanji = (iskanji(nc) && !(na & AF_TILE1));
1053 /* Handle unchanged grids */
1055 if ((na == oa) && (nc == oc) && (nta == ota) && (ntc == otc)&&
1056 (!kanji || (scr_aa[x + 1] == old_aa[x + 1] &&
1057 scr_cc[x + 1] == old_cc[x + 1] &&
1058 scr_taa[x + 1] == old_taa[x + 1] &&
1059 scr_tcc[x + 1] == old_tcc[x + 1])))
1061 if ((na == oa) && (nc == oc) && (nta == ota) && (ntc == otc))
1067 /* Draw pending chars (normal) */
1068 if (fa || always_text)
1070 (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
1073 /* Draw pending chars (black) */
1076 (void)((*Term->wipe_hook)(fx, y, fn));
1084 /* 全角文字の時は再開位置は+1 */
1096 /* Save new contents */
1103 /* 2nd byte of bigtile */
1104 if ((na & AF_BIGTILE2) == AF_BIGTILE2) continue;
1106 /* Handle high-bit attr/chars */
1107 if ((na & AF_TILE1) && (nc & 0x80))
1112 /* Draw pending chars (normal) */
1113 if (fa || always_text)
1115 (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
1118 /* Draw pending chars (black) */
1121 (void)((*Term->wipe_hook)(fx, y, fn));
1128 /* Hack -- Draw the special attr/char pair */
1129 (void)((*Term->pict_hook)(x, y, 1, &na, &nc, &nta, &ntc));
1135 /* Notice new color */
1137 if (fa != (na & AF_KANJIC))
1146 /* Draw the pending chars */
1147 if (fa || always_text)
1149 (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
1152 /* Hack -- Erase "leading" spaces */
1155 (void)((*Term->wipe_hook)(fx, y, fn));
1162 /* Save the new color */
1164 fa = (na & AF_KANJIC);
1171 /* Restart and Advance */
1172 if (fn++ == 0) fx = x;
1178 /* Draw pending chars (normal) */
1179 if (fa || always_text)
1181 (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
1184 /* Draw pending chars (black) */
1187 (void)((*Term->wipe_hook)(fx, y, fn));
1194 * Flush a row of the current window (see "Term_fresh")
1196 * Display text using "Term_text()" and "Term_wipe()"
1198 static void Term_fresh_row_text(TERM_LEN y, TERM_LEN x1, TERM_LEN x2)
1202 TERM_COLOR *old_aa = Term->old->a[y];
1203 char *old_cc = Term->old->c[y];
1205 TERM_COLOR *scr_aa = Term->scr->a[y];
1206 char *scr_cc = Term->scr->c[y];
1208 /* The "always_text" flag */
1209 int always_text = Term->always_text;
1211 /* Pending length */
1218 byte fa = Term->attr_blank;
1227 /* 全角文字の2バイト目かどうか */
1230 for (x = 0; x < x1; x++)
1231 if (!(old_aa[x] & AF_TILE1) && iskanji(old_cc[x]))
1242 /* Scan "modified" columns */
1243 for (x = x1; x <= x2; x++)
1245 /* See what is currently here */
1249 /* See what is desired there */
1263 /* 特殊文字としてMSBが立っている可能性がある */
1264 /* その場合attrのMSBも立っているのでこれで識別する */
1266 kanji = (iskanji(nc) && !(na & AF_TILE1));
1268 /* Handle unchanged grids */
1270 if ((na == oa) && (nc == oc) &&
1271 (!kanji || (scr_aa[x + 1] == old_aa[x + 1] &&
1272 scr_cc[x + 1] == old_cc[x + 1])))
1274 if ((na == oa) && (nc == oc))
1281 /* Draw pending chars (normal) */
1282 if (fa || always_text)
1284 (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
1287 /* Draw pending chars (black) */
1290 (void)((*Term->wipe_hook)(fx, y, fn));
1298 /* 全角文字の時は再開位置は+1 */
1310 /* Save new contents */
1314 /* Notice new color */
1316 if (fa != (na & AF_KANJIC))
1325 /* Draw the pending chars */
1326 if (fa || always_text)
1328 (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
1331 /* Hack -- Erase "leading" spaces */
1334 (void)((*Term->wipe_hook)(fx, y, fn));
1341 /* Save the new color */
1343 fa = (na & AF_KANJIC);
1350 /* Restart and Advance */
1351 if (fn++ == 0) fx = x;
1357 /* Draw pending chars (normal) */
1358 if (fa || always_text)
1360 (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
1363 /* Draw pending chars (black) */
1366 (void)((*Term->wipe_hook)(fx, y, fn));
1376 * Actually perform all requested changes to the window
1378 * If absolutely nothing has changed, not even temporarily, or if the
1379 * current "Term" is not mapped, then this function will return 1 and
1380 * do absolutely nothing.
1382 * Note that when "soft_cursor" is true, we erase the cursor (if needed)
1383 * whenever anything has changed, and redraw it (if needed) after all of
1384 * the screen updates are complete. This will induce a small amount of
1385 * "cursor flicker" but only when the screen has been updated. If the
1386 * screen is updated and then restored, you may still get this flicker.
1388 * When "soft_cursor" is not true, we make the cursor invisible before
1389 * doing anything else if it is supposed to be invisible by the time we
1390 * are done, and we make it visible after moving it to its final location
1391 * after all of the screen updates are complete.
1393 * Note that "Term_xtra(TERM_XTRA_CLEAR,0)" must erase the entire screen,
1394 * including the cursor, if needed, and may place the cursor anywhere.
1396 * Note that "Term_xtra(TERM_XTRA_FROSH,y)" will be always be called
1397 * after any row "y" has been "flushed", unless the "Term->never_frosh"
1398 * flag is set, and "Term_xtra(TERM_XTRA_FRESH,0)" will be called after
1399 * all of the rows have been "flushed".
1401 * Note the use of three different functions to handle the actual flush,
1402 * based on the settings of the "Term->always_pict" and "Term->higher_pict"
1403 * flags (see below).
1405 * The three helper functions (above) work by collecting similar adjacent
1406 * grids into stripes, and then sending each stripe to "Term->pict_hook",
1407 * "Term->text_hook", or "Term->wipe_hook", based on the settings of the
1408 * "Term->always_pict" and "Term->higher_pict" flags, which select which
1409 * of the helper functions to call to flush each row.
1411 * The helper functions currently "skip" any grids which already contain
1412 * the desired contents. This may or may not be the best method, especially
1413 * when the desired content fits nicely into the current stripe. For example,
1414 * it might be better to go ahead and queue them while allowed, but keep a
1415 * count of the "trailing skipables", then, when time to flush, or when a
1416 * "non skippable" is found, force a flush if there are too many skippables.
1418 * Perhaps an "initialization" stage, where the "text" (and "attr")
1419 * buffers are "filled" with information, converting "blanks" into
1420 * a convenient representation, and marking "skips" with "zero chars",
1421 * and then some "processing" is done to determine which chars to skip.
1423 * Currently, the helper functions are optimal for systems which prefer
1424 * to "print a char + move a char + print a char" to "print three chars",
1425 * and for applications that do a lot of "detailed" color printing.
1427 * In the two "queue" functions, total "non-changes" are "pre-skipped".
1428 * The helper functions must also handle situations in which the contents
1429 * of a grid are changed, but then changed back to the original value,
1430 * and situations in which two grids in the same row are changed, but
1431 * the grids between them are unchanged.
1433 * If the "Term->always_pict" flag is set, then "Term_fresh_row_pict()"
1434 * will be used instead of "Term_fresh_row_text()". This allows all the
1435 * modified grids to be collected into stripes of attr/char pairs, which
1436 * are then sent to the "Term->pict_hook" hook, which can draw these pairs
1437 * in whatever way it would like.
1439 * If the "Term->higher_pict" flag is set, then "Term_fresh_row_both()"
1440 * will be used instead of "Term_fresh_row_text()". This allows all the
1441 * "special" attr/char pairs (in which both the attr and char have the
1442 * high-bit set) to be sent (one pair at a time) to the "Term->pict_hook"
1443 * hook, which can draw these pairs in whatever way it would like.
1445 * Normally, the "Term_wipe()" function is used only to display "blanks"
1446 * that were induced by "Term_clear()" or "Term_erase()", and then only
1447 * if the "attr_blank" and "char_blank" fields have not been redefined
1448 * to use "white space" instead of the default "black space". Actually,
1449 * the "Term_wipe()" function is used to display all "black" text, such
1450 * as the default "spaces" created by "Term_clear()" and "Term_erase()".
1452 * Note that the "Term->always_text" flag will disable the use of the
1453 * "Term_wipe()" function hook entirely, and force all text, even text
1454 * drawn in the color "black", to be explicitly drawn. This is useful
1455 * for machines which implement "Term_wipe()" by just drawing spaces.
1457 * Note that the "Term->always_pict" flag will disable the use of the
1458 * "Term_wipe()" function entirely, and force everything, even text
1459 * drawn in the attr "black", to be explicitly drawn.
1461 * Note that if no "black" text is ever drawn, and if "attr_blank" is
1462 * not "zero", then the "Term_wipe" hook will never be used, even if
1463 * the "Term->always_text" flag is not set.
1465 * This function does nothing unless the "Term" is "mapped", which allows
1466 * certain systems to optimize the handling of "closed" windows.
1468 * On systems with a "soft" cursor, we must explicitly erase the cursor
1469 * before flushing the output, if needed, to prevent a "jumpy" refresh.
1470 * The actual method for this is horrible, but there is very little that
1471 * we can do to simplify it efficiently.
1473 * On systems with a "hard" cursor, we will "hide" the cursor before
1474 * flushing the output, if needed, to avoid a "flickery" refresh. It
1475 * would be nice to *always* hide the cursor during the refresh, but
1476 * this might be expensive (and/or ugly) on some machines.
1478 * The "Term->icky_corner" flag is used to avoid calling "Term_wipe()"
1479 * or "Term_pict()" or "Term_text()" on the bottom right corner of the
1480 * window, which might induce "scrolling" or other nasty stuff on old
1481 * dumb terminals. This flag is handled very efficiently. We assume
1482 * that the "Term_curs()" call will prevent placing the cursor in the
1483 * corner, if needed, though I doubt such placement is ever a problem.
1484 * Currently, the use of "Term->icky_corner" and "Term->soft_cursor"
1485 * together may result in undefined behavior.
1487 errr Term_fresh(void)
1497 term_win *old = Term->old;
1498 term_win *scr = Term->scr;
1500 /* Before initialize (Advice from Mr.shimitei)*/
1501 if (!old || !scr) return (1);
1503 /* Do nothing unless "mapped" */
1504 if (!Term->mapped_flag) return (1);
1507 /* Trivial Refresh */
1509 (scr->cu == old->cu) &&
1510 (scr->cv == old->cv) &&
1511 (scr->cx == old->cx) &&
1512 (scr->cy == old->cy) &&
1513 !(Term->total_erase))
1520 /* Handle "total erase" */
1521 if (Term->total_erase)
1523 byte na = Term->attr_blank;
1524 char nc = Term->char_blank;
1526 /* Physically erase the entire window */
1527 Term_xtra(TERM_XTRA_CLEAR, 0);
1529 /* Hack -- clear all "cursor" data */
1530 old->cv = old->cu = old->cx = old->cy = 0;
1533 for (y = 0; y < h; y++)
1535 TERM_COLOR *aa = old->a[y];
1536 char *cc = old->c[y];
1538 TERM_COLOR *taa = old->ta[y];
1539 char *tcc = old->tc[y];
1542 /* Wipe each column */
1543 for (x = 0; x < w; x++)
1545 /* Wipe each grid */
1554 /* Redraw every row */
1556 Term->y2 = y2 = h - 1;
1558 /* Redraw every column */
1559 for (y = 0; y < h; y++)
1562 Term->x2[y] = w - 1;
1565 /* Forget "total erase" */
1566 Term->total_erase = FALSE;
1570 /* Cursor update -- Erase old Cursor */
1571 if (Term->soft_cursor)
1573 /* Cursor was visible */
1574 if (!old->cu && old->cv)
1577 TERM_LEN tx = old->cx;
1578 TERM_LEN ty = old->cy;
1580 TERM_COLOR *old_aa = old->a[ty];
1581 char *old_cc = old->c[ty];
1583 TERM_COLOR *old_taa = old->ta[ty];
1584 char *old_tcc = old->tc[ty];
1586 TERM_COLOR ota = old_taa[tx];
1587 char otc = old_tcc[tx];
1590 if (tx + 1 < Term->wid && !(old_aa[tx] & AF_TILE1)
1591 && iskanji(old_cc[tx]))
1594 /* Hack -- use "Term_pict()" always */
1595 if (Term->always_pict)
1597 (void)((*Term->pict_hook)(tx, ty, csize, &old_aa[tx], &old_cc[tx], &ota, &otc));
1600 /* Hack -- use "Term_pict()" sometimes */
1601 else if (Term->higher_pict && (old_aa[tx] & AF_TILE1) && (old_cc[tx] & 0x80))
1603 (void)((*Term->pict_hook)(tx, ty, 1, &old_aa[tx], &old_cc[tx], &ota, &otc));
1607 * Hack -- restore the actual character
1608 * 元の文字の描画範囲がカーソルより小さいと、
1609 * 上書きされなかった部分がゴミとして残る。
1610 * wipe_hook でカーソルを消去して text_hook で書き直す。
1612 else if (old_aa[tx] || Term->always_text)
1614 (void)((*Term->wipe_hook)(tx, ty, 1));
1615 (void)((*Term->text_hook)(tx, ty, csize, (unsigned char) (old_aa[tx] & 0xf), &old_cc[tx]));
1618 /* Hack -- erase the grid */
1621 (void)((*Term->wipe_hook)(tx, ty, 1));
1626 /* Cursor Update -- Erase old Cursor */
1629 /* Cursor will be invisible */
1630 if (scr->cu || !scr->cv)
1632 /* Make the cursor invisible */
1633 Term_xtra(TERM_XTRA_SHAPE, 0);
1638 /* Something to update */
1641 /* Handle "icky corner" */
1642 if (Term->icky_corner)
1644 /* Avoid the corner */
1647 /* Avoid the corner */
1648 if (Term->x2[h - 1] > w - 2)
1650 /* Avoid the corner */
1651 Term->x2[h - 1] = w - 2;
1657 /* Scan the "modified" rows */
1658 for (y = y1; y <= y2; ++y)
1660 TERM_LEN x1 = Term->x1[y];
1661 TERM_LEN x2 = Term->x2[y];
1663 /* Flush each "modified" row */
1666 /* Always use "Term_pict()" */
1667 if (Term->always_pict)
1670 Term_fresh_row_pict(y, x1, x2);
1673 /* Sometimes use "Term_pict()" */
1674 else if (Term->higher_pict)
1677 Term_fresh_row_both(y, x1, x2);
1680 /* Never use "Term_pict()" */
1684 Term_fresh_row_text(y, x1, x2);
1687 /* This row is all done */
1688 Term->x1[y] = (byte_hack)w;
1691 /* Hack -- Flush that row (if allowed) */
1692 if (!Term->never_frosh) Term_xtra(TERM_XTRA_FROSH, y);
1696 /* No rows are invalid */
1697 Term->y1 = (byte_hack)h;
1702 /* Cursor update -- Show new Cursor */
1703 if (Term->soft_cursor)
1705 /* Draw the cursor */
1706 if (!scr->cu && scr->cv)
1709 if ((scr->cx + 1 < w) &&
1710 ((old->a[scr->cy][scr->cx + 1] & AF_BIGTILE2) == AF_BIGTILE2 ||
1711 (!(old->a[scr->cy][scr->cx] & AF_TILE1) &&
1712 iskanji(old->c[scr->cy][scr->cx]))))
1714 if ((scr->cx + 1 < w) && (old->a[scr->cy][scr->cx + 1] & AF_BIGTILE2) == AF_BIGTILE2)
1717 /* Double width cursor for the Bigtile mode */
1718 (void)((*Term->bigcurs_hook)(scr->cx, scr->cy));
1722 /* Call the cursor display routine */
1723 (void)((*Term->curs_hook)(scr->cx, scr->cy));
1728 /* Cursor Update -- Show new Cursor */
1731 /* The cursor is useless, hide it */
1734 /* Paranoia -- Put the cursor NEAR where it belongs */
1735 (void)((*Term->curs_hook)(w - 1, scr->cy));
1737 /* Make the cursor invisible */
1738 /* Term_xtra(TERM_XTRA_SHAPE, 0); */
1741 /* The cursor is invisible, hide it */
1744 /* Paranoia -- Put the cursor where it belongs */
1745 (void)((*Term->curs_hook)(scr->cx, scr->cy));
1747 /* Make the cursor invisible */
1748 /* Term_xtra(TERM_XTRA_SHAPE, 0); */
1751 /* The cursor is visible, display it correctly */
1754 /* Put the cursor where it belongs */
1755 (void)((*Term->curs_hook)(scr->cx, scr->cy));
1757 /* Make the cursor visible */
1758 Term_xtra(TERM_XTRA_SHAPE, 1);
1763 /* Save the "cursor state" */
1770 /* Actually flush the output */
1771 Term_xtra(TERM_XTRA_FRESH, 0);
1780 /*** Output routines ***/
1784 * Set the cursor visibility
1786 errr Term_set_cursor(int v)
1789 if (Term->scr->cv == v) return (1);
1792 Term->scr->cv = (bool_hack)v;
1800 * Place the cursor at a given location
1802 * Note -- "illegal" requests do not move the cursor.
1804 errr Term_gotoxy(TERM_LEN x, TERM_LEN y)
1810 if ((x < 0) || (x >= w)) return (-1);
1811 if ((y < 0) || (y >= h)) return (-1);
1813 /* Remember the cursor */
1814 Term->scr->cx = (byte_hack)x;
1815 Term->scr->cy = (byte_hack)y;
1817 /* The cursor is not useless */
1826 * At a given location, place an attr/char
1827 * Do not change the cursor position
1828 * No visual changes until "Term_fresh()".
1830 errr Term_draw(TERM_LEN x, TERM_LEN y, TERM_COLOR a, char c)
1835 /* Verify location */
1836 if ((x < 0) || (x >= w)) return (-1);
1837 if ((y < 0) || (y >= h)) return (-1);
1839 /* Paranoia -- illegal char */
1840 if (!c) return (-2);
1842 /* Queue it for later */
1843 Term_queue_char(x, y, a, c, 0, 0);
1851 * Using the given attr, add the given char at the cursor.
1853 * We return "-2" if the character is "illegal". XXX XXX
1855 * We return "-1" if the cursor is currently unusable.
1857 * We queue the given attr/char for display at the current
1858 * cursor location, and advance the cursor to the right,
1859 * marking it as unuable and returning "1" if it leaves
1860 * the screen, and otherwise returning "0".
1862 * So when this function, or the following one, return a
1863 * positive value, future calls to either function will
1864 * return negative ones.
1866 errr Term_addch(TERM_COLOR a, char c)
1868 TERM_LEN w = Term->wid;
1870 /* Handle "unusable" cursor */
1871 if (Term->scr->cu) return (-1);
1873 /* Paranoia -- no illegal chars */
1874 if (!c) return (-2);
1876 /* Queue the given character for display */
1877 Term_queue_char(Term->scr->cx, Term->scr->cy, a, c, 0, 0);
1879 /* Advance the cursor */
1883 if (Term->scr->cx < w) return (0);
1885 /* Note "Useless" cursor */
1888 /* Note "Useless" cursor */
1894 * Bigtile version of Term_addch().
1896 * If use_bigtile is FALSE, simply call Term_addch() .
1898 * Otherwise, queue a pair of attr/char for display at the current
1899 * cursor location, and advance the cursor to the right by two.
1901 errr Term_add_bigch(TERM_COLOR a, char c)
1903 if (!use_bigtile) return Term_addch(a, c);
1905 /* Handle "unusable" cursor */
1906 if (Term->scr->cu) return (-1);
1908 /* Paranoia -- no illegal chars */
1909 if (!c) return (-2);
1911 /* Queue the given character for display */
1912 Term_queue_bigchar(Term->scr->cx, Term->scr->cy, a, c, 0, 0);
1914 /* Advance the cursor */
1918 if (Term->scr->cx < Term->wid) return (0);
1920 /* Note "Useless" cursor */
1923 /* Note "Useless" cursor */
1929 * At the current location, using an attr, add a string
1931 * We also take a length "n", using negative values to imply
1932 * the largest possible value, and then we use the minimum of
1933 * this length and the "actual" length of the string as the
1934 * actual number of characters to attempt to display, never
1935 * displaying more characters than will actually fit, since
1936 * we do NOT attempt to "wrap" the cursor at the screen edge.
1938 * We return "-1" if the cursor is currently unusable.
1939 * We return "N" if we were "only" able to write "N" chars,
1940 * even if all of the given characters fit on the screen,
1941 * and mark the cursor as unusable for future attempts.
1943 * So when this function, or the preceding one, return a
1944 * positive value, future calls to either function will
1945 * return negative ones.
1947 errr Term_addstr(int n, TERM_COLOR a, concptr s)
1950 TERM_LEN w = Term->wid;
1953 /* Handle "unusable" cursor */
1954 if (Term->scr->cu) return (-1);
1956 /* Obtain maximal length */
1957 k = (n < 0) ? (w + 1) : n;
1959 /* Obtain the usable string length */
1960 for (n = 0; (n < k) && s[n]; n++) /* loop */;
1962 /* React to reaching the edge of the screen */
1963 if (Term->scr->cx + n >= w) res = n = w - Term->scr->cx;
1965 /* Queue the first "n" characters for display */
1966 Term_queue_chars(Term->scr->cx, Term->scr->cy, n, a, s);
1968 /* Advance the cursor */
1969 Term->scr->cx += (byte_hack)n;
1971 /* Hack -- Notice "Useless" cursor */
1972 if (res) Term->scr->cu = 1;
1974 /* Success (usually) */
1980 * Move to a location and, using an attr, add a char
1982 errr Term_putch(TERM_LEN x, TERM_LEN y, TERM_COLOR a, char c)
1987 if ((res = Term_gotoxy(x, y)) != 0) return (res);
1989 /* Then add the char */
1990 if ((res = Term_addch(a, c)) != 0) return (res);
1998 * Move to a location and, using an attr, add a string
2000 errr Term_putstr(TERM_LEN x, TERM_LEN y, int n, TERM_COLOR a, concptr s)
2005 if ((res = Term_gotoxy(x, y)) != 0) return (res);
2007 /* Then add the string */
2008 if ((res = Term_addstr(n, a, s)) != 0) return (res);
2016 * Move to a location and, using an attr, add a string vertically
2018 errr Term_putstr_v(TERM_LEN x, TERM_LEN y, int n, byte a, concptr s)
2025 for (i = 0; i < n && s[i] != 0; i++)
2028 if ((res = Term_gotoxy(x, y0)) != 0) return (res);
2032 if ((res = Term_addstr(2, a, &s[i])) != 0) return (res);
2035 if (s[i] == 0) break;
2037 if ((res = Term_addstr(1, a, &s[i])) != 0) return (res);
2048 * Place cursor at (x,y), and clear the next "n" chars
2050 errr Term_erase(TERM_LEN x, TERM_LEN y, int n)
2054 TERM_LEN w = Term->wid;
2055 /* int h = Term->hgt; */
2060 int na = Term->attr_blank;
2061 int nc = Term->char_blank;
2066 TERM_COLOR *scr_taa;
2070 if (Term_gotoxy(x, y)) return (-1);
2072 /* Force legal size */
2073 if (x + n > w) n = w - x;
2076 scr_aa = Term->scr->a[y];
2077 scr_cc = Term->scr->c[y];
2079 scr_taa = Term->scr->ta[y];
2080 scr_tcc = Term->scr->tc[y];
2084 * 全角文字の右半分から文字を表示する場合、
2087 if (n > 0 && (((scr_aa[x] & AF_KANJI2) && !(scr_aa[x] & AF_TILE1))
2088 || (scr_aa[x] & AF_BIGTILE2) == AF_BIGTILE2))
2090 if (n > 0 && (scr_aa[x] & AF_BIGTILE2) == AF_BIGTILE2)
2097 /* Scan every column */
2098 for (i = 0; i < n; i++, x++)
2103 /* Hack -- Ignore "non-changes" */
2104 if ((oa == na) && (oc == nc)) continue;
2108 * 全角文字の左半分で表示を終了する場合、
2111 * 2001/04/29 -- Habu
2112 * 行の右端の場合はこの処理をしないように修正。
2114 if ((oa & AF_KANJI1) && (i + 1) == n && x != w - 1)
2117 /* Save the "literal" information */
2118 scr_aa[x] = (byte_hack)na;
2119 scr_cc[x] = (char)nc;
2124 /* Track minimum changed column */
2127 /* Track maximum changed column */
2131 /* Expand the "change area" as needed */
2134 /* Check for new min/max row info */
2135 if (y < Term->y1) Term->y1 = (byte_hack)y;
2136 if (y > Term->y2) Term->y2 = (byte_hack)y;
2138 /* Check for new min/max col info in this row */
2139 if (x1 < Term->x1[y]) Term->x1[y] = (byte_hack)x1;
2140 if (x2 > Term->x2[y]) Term->x2[y] = (byte_hack)x2;
2149 * Clear the entire window, and move to the top left corner
2151 * Note the use of the special "total_erase" code
2153 errr Term_clear(void)
2157 TERM_LEN w = Term->wid;
2158 TERM_LEN h = Term->hgt;
2160 TERM_COLOR na = Term->attr_blank;
2161 char nc = Term->char_blank;
2166 /* Cursor to the top left */
2167 Term->scr->cx = Term->scr->cy = 0;
2170 for (y = 0; y < h; y++)
2172 TERM_COLOR *scr_aa = Term->scr->a[y];
2173 char *scr_cc = Term->scr->c[y];
2175 TERM_COLOR *scr_taa = Term->scr->ta[y];
2176 char *scr_tcc = Term->scr->tc[y];
2178 /* Wipe each column */
2179 for (x = 0; x < w; x++)
2188 /* This row has changed */
2190 Term->x2[y] = w - 1;
2193 /* Every row has changed */
2197 /* Force "total erase" */
2198 Term->total_erase = TRUE;
2209 * Redraw (and refresh) the whole window.
2211 errr Term_redraw(void)
2213 /* Force "total erase" */
2214 Term->total_erase = TRUE;
2216 /* Hack -- Refresh */
2225 * Redraw part of a window.
2227 errr Term_redraw_section(TERM_LEN x1, TERM_LEN y1, TERM_LEN x2, TERM_LEN y2)
2233 /* Bounds checking */
2234 if (y2 >= Term->hgt) y2 = Term->hgt - 1;
2235 if (x2 >= Term->wid) x2 = Term->wid - 1;
2240 Term->y1 = (byte_hack)y1;
2241 Term->y2 = (byte_hack)y2;
2243 /* Set the x limits */
2244 for (i = Term->y1; i <= Term->y2; i++)
2252 if (Term->scr->a[i][x1j] & AF_KANJI2) x1j--;
2255 if (x2j < Term->wid - 1)
2257 if (Term->scr->a[i][x2j] & AF_KANJI1) x2j++;
2260 Term->x1[i] = (byte_hack)x1j;
2261 Term->x2[i] = (byte_hack)x2j;
2263 g_ptr = Term->old->c[i];
2265 /* Clear the section so it is redrawn */
2266 for (j = x1j; j <= x2j; j++)
2268 /* Hack - set the old character to "none" */
2275 g_ptr = Term->old->c[i];
2277 /* Clear the section so it is redrawn */
2278 for (j = x1; j <= x2; j++)
2280 /* Hack - set the old character to "none" */
2286 /* Hack -- Refresh */
2295 /*** Access routines ***/
2299 * Extract the cursor visibility
2301 errr Term_get_cursor(int *v)
2303 /* Extract visibility */
2304 (*v) = Term->scr->cv;
2312 * Extract the current window size
2314 errr Term_get_size(TERM_LEN *w, TERM_LEN *h)
2316 /* Access the cursor */
2326 * Extract the current cursor location
2328 errr Term_locate(TERM_LEN *x, TERM_LEN *y)
2330 /* Access the cursor */
2331 (*x) = Term->scr->cx;
2332 (*y) = Term->scr->cy;
2334 /* Warn about "useless" cursor */
2335 if (Term->scr->cu) return (1);
2343 * At a given location, determine the "current" attr and char
2344 * Note that this refers to what will be on the window after the
2345 * next call to "Term_fresh()". It may or may not already be there.
2347 errr Term_what(TERM_LEN x, TERM_LEN y, TERM_COLOR *a, char *c)
2349 TERM_LEN w = Term->wid;
2350 TERM_LEN h = Term->hgt;
2352 /* Verify location */
2353 if ((x < 0) || (x >= w)) return (-1);
2354 if ((y < 0) || (y >= h)) return (-1);
2357 (*a) = Term->scr->a[y][x];
2358 (*c) = Term->scr->c[y][x];
2366 /*** Input routines ***/
2370 * Flush and forget the input
2372 errr Term_flush(void)
2374 /* Hack -- Flush all events */
2375 Term_xtra(TERM_XTRA_FLUSH, 0);
2377 /* Forget all keypresses */
2378 Term->key_head = Term->key_tail = 0;
2387 * Add a keypress to the "queue"
2389 errr Term_keypress(int k)
2391 /* Hack -- Refuse to enqueue non-keys */
2392 if (!k) return (-1);
2394 /* Store the char, advance the queue */
2395 Term->key_queue[Term->key_head++] = (char)k;
2397 /* Circular queue, handle wrap */
2398 if (Term->key_head == Term->key_size) Term->key_head = 0;
2400 /* Success (unless overflow) */
2401 if (Term->key_head != Term->key_tail) return (0);
2404 /* Hack -- Forget the oldest key */
2405 if (++Term->key_tail == Term->key_size) Term->key_tail = 0;
2414 * Add a keypress to the FRONT of the "queue"
2416 errr Term_key_push(int k)
2418 /* Hack -- Refuse to enqueue non-keys */
2419 if (!k) return (-1);
2421 /* Hack -- Overflow may induce circular queue */
2422 if (Term->key_tail == 0) Term->key_tail = Term->key_size;
2424 /* Back up, Store the char */
2425 Term->key_queue[--Term->key_tail] = (char)k;
2427 /* Success (unless overflow) */
2428 if (Term->key_head != Term->key_tail) return (0);
2439 * Check for a pending keypress on the key queue.
2441 * Store the keypress, if any, in "ch", and return "0".
2442 * Otherwise store "zero" in "ch", and return "1".
2444 * Wait for a keypress if "wait" is true.
2446 * Remove the keypress if "take" is true.
2448 errr Term_inkey(char *ch, bool wait, bool take)
2457 /* Hack -- get bored */
2458 if (!Term->never_bored)
2460 /* Process random events */
2461 Term_xtra(TERM_XTRA_BORED, 0);
2467 /* Process pending events while necessary */
2468 while (Term->key_head == Term->key_tail)
2470 /* Process events (wait for one) */
2471 Term_xtra(TERM_XTRA_EVENT, TRUE);
2478 /* Process pending events if necessary */
2479 if (Term->key_head == Term->key_tail)
2481 /* Process events (do not wait) */
2482 Term_xtra(TERM_XTRA_EVENT, FALSE);
2486 /* No keys are ready */
2487 if (Term->key_head == Term->key_tail) return (1);
2489 /* Extract the next keypress */
2490 (*ch) = Term->key_queue[Term->key_tail];
2492 /* If requested, advance the queue, wrap around if necessary */
2493 if (take && (++Term->key_tail == Term->key_size)) Term->key_tail = 0;
2501 /*** Extra routines ***/
2505 * Save the "requested" screen into the "memorized" screen
2507 * Every "Term_save()" should match exactly one "Term_load()"
2509 errr Term_save(void)
2511 TERM_LEN w = Term->wid;
2512 TERM_LEN h = Term->hgt;
2517 /* Allocate window */
2518 MAKE(Term->mem, term_win);
2520 /* Initialize window */
2521 term_win_init(Term->mem, w, h);
2525 term_win_copy(Term->mem, Term->scr, w, h);
2533 * Restore the "requested" contents (see above).
2535 * Every "Term_save()" should match exactly one "Term_load()"
2537 errr Term_load(void)
2541 TERM_LEN w = Term->wid;
2542 TERM_LEN h = Term->hgt;
2547 /* Allocate window */
2548 MAKE(Term->mem, term_win);
2550 /* Initialize window */
2551 term_win_init(Term->mem, w, h);
2555 term_win_copy(Term->scr, Term->mem, w, h);
2558 for (y = 0; y < h; y++)
2562 Term->x2[y] = w - 1;
2575 * Exchange the "requested" screen with the "tmp" screen
2577 errr Term_exchange(void)
2581 TERM_LEN w = Term->wid;
2582 TERM_LEN h = Term->hgt;
2584 term_win *exchanger;
2590 /* Allocate window */
2591 MAKE(Term->tmp, term_win);
2593 /* Initialize window */
2594 term_win_init(Term->tmp, w, h);
2598 exchanger = Term->scr;
2599 Term->scr = Term->tmp;
2600 Term->tmp = exchanger;
2603 for (y = 0; y < h; y++)
2607 Term->x2[y] = w - 1;
2621 * React to a new physical window size.
2623 errr Term_resize(TERM_LEN w, TERM_LEN h)
2637 /* Resizing is forbidden */
2638 if (Term->fixed_shape) return (-1);
2640 /* Ignore illegal changes */
2641 if ((w < 1) || (h < 1)) return (-1);
2644 /* Ignore non-changes */
2645 if ((Term->wid == w) && (Term->hgt == h) && (arg_bigtile == use_bigtile))
2648 use_bigtile = arg_bigtile;
2650 /* Minimum dimensions */
2651 wid = MIN(Term->wid, w);
2652 hgt = MIN(Term->hgt, h);
2658 /* Save old window */
2659 hold_old = Term->old;
2661 /* Save old window */
2662 hold_scr = Term->scr;
2664 /* Save old window */
2665 hold_mem = Term->mem;
2667 /* Save old window */
2668 hold_tmp = Term->tmp;
2670 /* Create new scanners */
2671 C_MAKE(Term->x1, h, TERM_LEN);
2672 C_MAKE(Term->x2, h, TERM_LEN);
2674 /* Create new window */
2675 MAKE(Term->old, term_win);
2677 /* Initialize new window */
2678 term_win_init(Term->old, w, h);
2680 /* Save the contents */
2681 term_win_copy(Term->old, hold_old, wid, hgt);
2683 /* Create new window */
2684 MAKE(Term->scr, term_win);
2686 /* Initialize new window */
2687 term_win_init(Term->scr, w, h);
2689 /* Save the contents */
2690 term_win_copy(Term->scr, hold_scr, wid, hgt);
2695 /* Create new window */
2696 MAKE(Term->mem, term_win);
2698 /* Initialize new window */
2699 term_win_init(Term->mem, w, h);
2701 /* Save the contents */
2702 term_win_copy(Term->mem, hold_mem, wid, hgt);
2708 /* Create new window */
2709 MAKE(Term->tmp, term_win);
2711 /* Initialize new window */
2712 term_win_init(Term->tmp, w, h);
2714 /* Save the contents */
2715 term_win_copy(Term->tmp, hold_tmp, wid, hgt);
2718 /* Free some arrays */
2719 C_KILL(hold_x1, Term->hgt, TERM_LEN);
2720 C_KILL(hold_x2, Term->hgt, TERM_LEN);
2723 term_win_nuke(hold_old, Term->wid, Term->hgt);
2726 KILL(hold_old, term_win);
2728 /* Illegal cursor */
2729 if (Term->old->cx >= w) Term->old->cu = 1;
2730 if (Term->old->cy >= h) Term->old->cu = 1;
2733 term_win_nuke(hold_scr, Term->wid, Term->hgt);
2736 KILL(hold_scr, term_win);
2738 /* Illegal cursor */
2739 if (Term->scr->cx >= w) Term->scr->cu = 1;
2740 if (Term->scr->cy >= h) Term->scr->cu = 1;
2746 term_win_nuke(hold_mem, Term->wid, Term->hgt);
2749 KILL(hold_mem, term_win);
2751 /* Illegal cursor */
2752 if (Term->mem->cx >= w) Term->mem->cu = 1;
2753 if (Term->mem->cy >= h) Term->mem->cu = 1;
2760 term_win_nuke(hold_tmp, Term->wid, Term->hgt);
2763 KILL(hold_tmp, term_win);
2765 /* Illegal cursor */
2766 if (Term->tmp->cx >= w) Term->tmp->cu = 1;
2767 if (Term->tmp->cy >= h) Term->tmp->cu = 1;
2774 /* Force "total erase" */
2775 Term->total_erase = TRUE;
2778 for (i = 0; i < h; i++)
2782 Term->x2[i] = w - 1;
2789 /* Execute the "resize_hook" hook, if available */
2790 if (Term->resize_hook)
2792 Term->resize_hook();
2802 * Activate a new Term (and deactivate the current Term)
2804 * This function is extremely important, and also somewhat bizarre.
2805 * It is the only function that should "modify" the value of "Term".
2807 * To "create" a valid "term", one should do "term_init(t)", then
2808 * set the various flags and hooks, and then do "Term_activate(t)".
2810 errr Term_activate(term *t)
2812 /* Hack -- already done */
2813 if (Term == t) return (1);
2815 /* Deactivate the old Term */
2816 if (Term) Term_xtra(TERM_XTRA_LEVEL, 0);
2818 /* Hack -- Call the special "init" hook */
2819 if (t && !t->active_flag)
2821 /* Call the "init" hook */
2822 if (t->init_hook) (*t->init_hook)(t);
2825 t->active_flag = TRUE;
2828 t->mapped_flag = TRUE;
2831 /* Remember the Term */
2834 /* Activate the new Term */
2835 if (Term) Term_xtra(TERM_XTRA_LEVEL, 1);
2846 errr term_nuke(term *t)
2848 TERM_LEN w = t->wid;
2849 TERM_LEN h = t->hgt;
2852 /* Hack -- Call the special "nuke" hook */
2855 /* Call the "nuke" hook */
2856 if (t->nuke_hook) (*t->nuke_hook)(t);
2859 t->active_flag = FALSE;
2861 /* Assume not mapped */
2862 t->mapped_flag = FALSE;
2866 /* Nuke "displayed" */
2867 term_win_nuke(t->old, w, h);
2869 /* Kill "displayed" */
2870 KILL(t->old, term_win);
2872 /* Nuke "requested" */
2873 term_win_nuke(t->scr, w, h);
2875 /* Kill "requested" */
2876 KILL(t->scr, term_win);
2881 /* Nuke "memorized" */
2882 term_win_nuke(t->mem, w, h);
2884 /* Kill "memorized" */
2885 KILL(t->mem, term_win);
2891 /* Nuke "temporary" */
2892 term_win_nuke(t->tmp, w, h);
2894 /* Kill "temporary" */
2895 KILL(t->tmp, term_win);
2898 /* Free some arrays */
2899 C_KILL(t->x1, h, TERM_LEN);
2900 C_KILL(t->x2, h, TERM_LEN);
2902 /* Free the input queue */
2903 C_KILL(t->key_queue, t->key_size, char);
2911 * Initialize a term, using a window of the given size.
2912 * Also prepare the "input queue" for "k" keypresses
2913 * By default, the cursor starts out "invisible"
2914 * By default, we "erase" using "black spaces"
2916 errr term_init(term *t, TERM_LEN w, TERM_LEN h, int k)
2922 (void)WIPE(t, term);
2925 /* Prepare the input queue */
2926 t->key_head = t->key_tail = 0;
2928 /* Determine the input queue size */
2929 t->key_size = (u16b)k;
2931 /* Allocate the input queue */
2932 C_MAKE(t->key_queue, t->key_size, char);
2939 /* Allocate change arrays */
2940 C_MAKE(t->x1, h, TERM_LEN);
2941 C_MAKE(t->x2, h, TERM_LEN);
2944 /* Allocate "displayed" */
2945 MAKE(t->old, term_win);
2947 /* Initialize "displayed" */
2948 term_win_init(t->old, w, h);
2951 /* Allocate "requested" */
2952 MAKE(t->scr, term_win);
2954 /* Initialize "requested" */
2955 term_win_init(t->scr, w, h);
2959 for (y = 0; y < h; y++)
2970 /* Force "total erase" */
2971 t->total_erase = TRUE;
2974 /* Default "blank" */
2976 t->char_blank = ' ';
2979 /* Prepare "fake" hooks to prevent core dumps */
2980 t->curs_hook = Term_curs_hack;
2981 t->bigcurs_hook = Term_bigcurs_hack;
2982 t->wipe_hook = Term_wipe_hack;
2983 t->text_hook = Term_text_hack;
2984 t->pict_hook = Term_pict_hack;