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_BIGTILE 0xf0
22 #define AF_KANJI1 0x10
23 #define AF_KANJI2 0x20
24 #define AF_KANJIC 0x0f
27 * °À¤ËÁ´³Ñʸ»ú¤Î£±¥Ð¥¤¥ÈÌÜ¡¢£²¥Ð¥¤¥ÈÌܤ⵲±¡£
32 * This file provides a generic, efficient, terminal window package,
33 * which can be used not only on standard terminal environments such
34 * as dumb terminals connected to a Unix box, but also in more modern
35 * "graphic" environments, such as the Macintosh or Unix/X11.
37 * Each "window" works like a standard "dumb terminal", that is, it
38 * can display a two dimensional array of grids containing colored
39 * textual symbols, plus an optional cursor, and it can be used to
40 * get keypress events from the user.
42 * In fact, this package can simply be used, if desired, to support
43 * programs which will look the same on a dumb terminal as they do
44 * on a graphic platform such as the Macintosh.
46 * This package was designed to help port the game "Angband" to a wide
47 * variety of different platforms. Angband, like many other games in
48 * the "rogue-like" heirarchy, requires, at the minimum, the ability
49 * to display "colored textual symbols" in a standard 80x24 "window",
50 * such as that provided by most dumb terminals, and many old personal
51 * computers, and to check for "keypresses" from the user. The major
52 * concerns were thus portability and efficiency, so Angband could be
53 * easily ported to many different systems, with minimal effort, and
54 * yet would run quickly on each of these systems, no matter what kind
55 * of underlying hardware/software support was being used.
57 * It is important to understand the differences between the older
58 * "dumb terminals" and the newer "graphic interface" machines, since
59 * this package was designed to work with both types of systems.
62 * waiting for a keypress is complex
63 * checking for a keypress is often cheap
64 * changing "colors" may be expensive
65 * the "color" of a "blank" is rarely important
66 * moving the "cursor" is relatively cheap
67 * use a "software" cursor (only moves when requested)
68 * drawing characters normally will not erase old ones
69 * drawing a character on the cursor often erases it
70 * may have fast routines for "clear a region"
71 * the bottom right corner is usually not special
74 * waiting for a keypress is simple
75 * checking for a keypress is often expensive
76 * changing "colors" is usually cheap
77 * the "color" of a "blank" may be important
78 * moving the "cursor" may be expensive
79 * use a "hardware" cursor (moves during screen updates)
80 * drawing new symbols automatically erases old ones
81 * characters may only be drawn at the cursor location
82 * drawing a character on the cursor will move the cursor
83 * may have fast routines for "clear entire window"
84 * may have fast routines for "clear to end of line"
85 * the bottom right corner is often dangerous
88 * This package provides support for multiple windows, each of an
89 * arbitrary size (up to 255x255), each with its own set of flags,
90 * and its own hooks to handle several low-level procedures which
91 * differ from platform to platform. Then the main program simply
92 * creates one or more "term" structures, setting the various flags
93 * and hooks in a manner appropriate for the current platform, and
94 * then it can use the various "term" structures without worrying
95 * about the underlying platform.
98 * This package allows each "grid" in each window to hold an attr/char
99 * pair, with each ranging from 0 to 255, and makes very few assumptions
100 * about the meaning of any attr/char values. Normally, we assume that
101 * "attr 0" is "black", with the semantics that "black" text should be
102 * sent to "Term_wipe()" instead of "Term_text()", but this sematics is
103 * modified if either the "always_pict" or the "always_text" flags are
104 * set. We assume that "char 0" is "dangerous", since placing such a
105 * "char" in the middle of a string "terminates" the string, and usually
106 * we prevent its use.
108 * Finally, we use a special attr/char pair, defaulting to "attr 0" and
109 * "char 32", also known as "black space", when we "erase" or "clear"
110 * any window, but this pair can be redefined to any pair, including
111 * the standard "white space", or the bizarre "emptiness" ("attr 0"
112 * and "char 0"), as long as various obscure restrictions are met.
115 * This package provides several functions which allow a program to
116 * interact with the "term" structures. Most of the functions allow
117 * the program to "request" certain changes to the current "term",
118 * such as moving the cursor, drawing an attr/char pair, erasing a
119 * region of grids, hiding the cursor, etc. Then there is a special
120 * function which causes all of the "pending" requests to be performed
121 * in an efficient manner. There is another set of functions which
122 * allow the program to query the "requested state" of the current
123 * "term", such as asking for the cursor location, or what attr/char
124 * is at a given location, etc. There is another set of functions
125 * dealing with "keypress" events, which allows the program to ask if
126 * the user has pressed any keys, or to forget any keys the user pressed.
127 * There is a pair of functions to allow this package to memorize the
128 * contents of the current "term", and to restore these contents at
129 * a later time. There is a special function which allows the program
130 * to specify which "term" structure should be the "current" one. At
131 * the lowest level, there is a set of functions which allow a new
132 * "term" to be initialized or destroyed, and which allow this package,
133 * or a program, to access the special "hooks" defined for the current
134 * "term", and a set of functions which those "hooks" can use to inform
135 * this package of the results of certain occurances, for example, one
136 * such function allows this package to learn about user keypresses,
137 * detected by one of the special "hooks".
139 * We provide, among other things, the functions "Term_keypress()"
140 * to "react" to keypress events, and "Term_redraw()" to redraw the
141 * entire window, plus "Term_resize()" to note a new size.
144 * Note that the current "term" contains two "window images". One of
145 * these images represents the "requested" contents of the "term", and
146 * the other represents the "actual" contents of the "term", at the time
147 * of the last performance of pending requests. This package uses these
148 * two images to determine the "minimal" amount of work needed to make
149 * the "actual" contents of the "term" match the "requested" contents of
150 * the "term". This method is not perfect, but it often reduces the
151 * amount of work needed to perform the pending requests, which thus
152 * increases the speed of the program itself. This package promises
153 * that the requested changes will appear to occur either "all at once"
154 * or in a "top to bottom" order. In addition, a "cursor" is maintained,
155 * and this cursor is updated along with the actual window contents.
157 * Currently, the "Term_fresh()" routine attempts to perform the "minimum"
158 * number of physical updates, in terms of total "work" done by the hooks
159 * Term_wipe(), Term_text(), and Term_pict(), making use of the fact that
160 * adjacent characters of the same color can both be drawn together using
161 * the "Term_text()" hook, and that "black" text can often be sent to the
162 * "Term_wipe()" hook instead of the "Term_text()" hook, and if something
163 * is already displayed in a window, then it is not necessary to display
164 * it again. Unfortunately, this may induce slightly non-optimal results
165 * in some cases, in particular, those in which, say, a string of ten
166 * characters needs to be written, but the fifth character has already
167 * been displayed. Currently, this will cause the "Term_text()" routine
168 * to be called once for each half of the string, instead of once for the
169 * whole string, which, on some machines, may be non-optimal behavior.
171 * The new formalism includes a "displayed" screen image (old) which
172 * is actually seen by the user, a "requested" screen image (scr)
173 * which is being prepared for display, a "memorized" screen image
174 * (mem) which is used to save and restore screen images, and a
175 * "temporary" screen image (tmp) which is currently unused.
178 * Several "flags" are available in each "term" to allow the underlying
179 * visual system (which initializes the "term" structure) to "optimize"
180 * the performance of this package for the given system, or to request
181 * certain behavior which is helpful/required for the given system.
183 * The "soft_cursor" flag indicates the use of a "soft" cursor, which
184 * only moves when explicitly requested,and which is "erased" when
185 * any characters are drawn on top of it. This flag is used for all
186 * "graphic" systems which handle the cursor by "drawing" it.
188 * The "icky_corner" flag indicates that the bottom right "corner"
189 * of the windows are "icky", and "printing" anything there may
190 * induce "messy" behavior, such as "scrolling". This flag is used
191 * for most old "dumb terminal" systems.
194 * The "term" structure contains the following function "hooks":
196 * Term->init_hook = Init the term
197 * Term->nuke_hook = Nuke the term
198 * Term->user_hook = Perform user actions
199 * Term->xtra_hook = Perform extra actions
200 * Term->curs_hook = Draw (or Move) the cursor
201 * Term->bigcurs_hook = Draw (or Move) the big cursor (bigtile mode)
202 * Term->wipe_hook = Draw some blank spaces
203 * Term->text_hook = Draw some text in the window
204 * Term->pict_hook = Draw some attr/chars in the window
206 * The "Term->user_hook" hook provides a simple hook to an implementation
207 * defined function, with application defined semantics. It is available
208 * to the program via the "Term_user()" function.
210 * The "Term->xtra_hook" hook provides a variety of different functions,
211 * based on the first parameter (which should be taken from the various
212 * TERM_XTRA_* defines) and the second parameter (which may make sense
213 * only for some first parameters). It is available to the program via
214 * the "Term_xtra()" function, though some first parameters are only
215 * "legal" when called from inside this package.
217 * The "Term->curs_hook" hook provides this package with a simple way
218 * to "move" or "draw" the cursor to the grid "x,y", depending on the
219 * setting of the "soft_cursor" flag. Note that the cursor is never
220 * redrawn if "nothing" has happened to the screen (even temporarily).
221 * This hook is required.
223 * The "Term->wipe_hook" hook provides this package with a simple way
224 * to "erase", starting at "x,y", the next "n" grids. This hook assumes
225 * that the input is valid. This hook is required, unless the setting
226 * of the "always_pict" or "always_text" flags makes it optional.
228 * The "Term->text_hook" hook provides this package with a simple way
229 * to "draw", starting at "x,y", the "n" chars contained in "cp", using
230 * the attr "a". This hook assumes that the input is valid, and that
231 * "n" is between 1 and 256 inclusive, but it should NOT assume that
232 * the contents of "cp" are null-terminated. This hook is required,
233 * unless the setting of the "always_pict" flag makes it optional.
235 * The "Term->pict_hook" hook provides this package with a simple way
236 * to "draw", starting at "x,y", the "n" attr/char pairs contained in
237 * the arrays "ap" and "cp". This hook assumes that the input is valid,
238 * and that "n" is between 1 and 256 inclusive, but it should NOT assume
239 * that the contents of "cp" are null-terminated. This hook is optional,
240 * unless the setting of the "always_pict" or "higher_pict" flags make
241 * it required. Note that recently, this hook was changed from taking
242 * a byte "a" and a char "c" to taking a length "n", an array of bytes
243 * "ap" and an array of chars "cp". Old implementations of this hook
244 * should now iterate over all "n" attr/char pairs.
247 * The game "Angband" uses a set of files called "main-xxx.c", for
248 * various "xxx" suffixes. Most of these contain a function called
249 * "init_xxx()", that will prepare the underlying visual system for
250 * use with Angband, and then create one or more "term" structures,
251 * using flags and hooks appropriate to the given platform, so that
252 * the "main()" function can call one (or more) of the "init_xxx()"
253 * functions, as appropriate, to prepare the required "term" structs
254 * (one for each desired sub-window), and these "init_xxx()" functions
255 * are called from a centralized "main()" function in "main.c". Other
256 * "main-xxx.c" systems contain their own "main()" function which, in
257 * addition to doing everything needed to initialize the actual program,
258 * also does everything that the normal "init_xxx()" functions would do.
260 * The game "Angband" defines, in addition to "attr 0", all of the
261 * attr codes from 1 to 15, using definitions in "defines.h", and
262 * thus the "main-xxx.c" files used by Angband must handle these
263 * attr values correctly. Also, they must handle all other attr
264 * values, though they may do so in any way they wish, for example,
265 * by always taking every attr code mod 16. Many of the "main-xxx.c"
266 * files use "white space" ("attr 1" / "char 32") to "erase" or "clear"
267 * any window, for efficiency.
269 * The game "Angband" uses the "Term_user" hook to allow any of the
270 * "main-xxx.c" files to interact with the user, by calling this hook
271 * whenever the user presses the "!" key when the game is waiting for
272 * a new command. This could be used, for example, to provide "unix
273 * shell commands" to the Unix versions of the game.
275 * See "main-xxx.c" for a simple skeleton file which can be used to
276 * create a "visual system" for a new platform when porting Angband.
292 /*** Local routines ***/
296 * Nuke a term_win (see below)
298 static errr term_win_nuke(term_win *s, int w, int h)
300 /* Free the window access arrays */
301 C_KILL(s->a, h, byte*);
302 C_KILL(s->c, h, char*);
304 /* Free the window content arrays */
305 C_KILL(s->va, h * w, byte);
306 C_KILL(s->vc, h * w, char);
308 /* Free the terrain access arrays */
309 C_KILL(s->ta, h, byte*);
310 C_KILL(s->tc, h, char*);
312 /* Free the terrain content arrays */
313 C_KILL(s->vta, h * w, byte);
314 C_KILL(s->vtc, h * w, char);
322 * Initialize a "term_win" (using the given window size)
324 static errr term_win_init(term_win *s, int w, int h)
328 /* Make the window access arrays */
329 C_MAKE(s->a, h, byte*);
330 C_MAKE(s->c, h, char*);
332 /* Make the window content arrays */
333 C_MAKE(s->va, h * w, byte);
334 C_MAKE(s->vc, h * w, char);
336 /* Make the terrain access arrays */
337 C_MAKE(s->ta, h, byte*);
338 C_MAKE(s->tc, h, char*);
340 /* Make the terrain content arrays */
341 C_MAKE(s->vta, h * w, byte);
342 C_MAKE(s->vtc, h * w, char);
345 /* Prepare the window access arrays */
346 for (y = 0; y < h; y++)
348 s->a[y] = s->va + w * y;
349 s->c[y] = s->vc + w * y;
351 s->ta[y] = s->vta + w * y;
352 s->tc[y] = s->vtc + w * y;
361 * Copy a "term_win" from another
363 static errr term_win_copy(term_win *s, term_win *f, int w, int h)
368 for (y = 0; y < h; y++)
370 byte *f_aa = f->a[y];
371 char *f_cc = f->c[y];
373 byte *s_aa = s->a[y];
374 char *s_cc = s->c[y];
376 byte *f_taa = f->ta[y];
377 char *f_tcc = f->tc[y];
379 byte *s_taa = s->ta[y];
380 char *s_tcc = s->tc[y];
382 for (x = 0; x < w; x++)
404 /*** External hooks ***/
408 * Execute the "Term->user_hook" hook, if available (see above).
410 errr Term_user(int n)
412 /* Verify the hook */
413 if (!Term->user_hook) return (-1);
416 return ((*Term->user_hook)(n));
420 * Execute the "Term->xtra_hook" hook, if available (see above).
422 errr Term_xtra(int n, int v)
424 /* Verify the hook */
425 if (!Term->xtra_hook) return (-1);
428 if( n == TERM_XTRA_CLEAR || n == TERM_XTRA_FRESH || n == TERM_XTRA_SHAPE )
429 send_xtra_to_chuukei_server(n);
432 return ((*Term->xtra_hook)(n, v));
441 * Hack -- fake hook for "Term_curs()" (see above)
443 static errr Term_curs_hack(int x, int y)
445 /* Compiler silliness */
446 if (x || y) return (-2);
453 * Hack -- fake hook for "Term_wipe()" (see above)
455 static errr Term_wipe_hack(int x, int y, int n)
457 /* Compiler silliness */
458 if (x || y || n) return (-2);
465 * Hack -- fake hook for "Term_text()" (see above)
468 static errr Term_text_hack(int x, int y, int n, byte a, cptr cp)
470 static errr Term_text_hack(int x, int y, int n, byte a, const char *cp)
474 /* Compiler silliness */
475 if (x || y || n || a || cp) return (-2);
482 * Hack -- fake hook for "Term_pict()" (see above)
484 static errr Term_pict_hack(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp)
486 /* Compiler silliness */
487 if (x || y || n || ap || cp || tap || tcp) return (-2);
495 /*** Efficient routines ***/
499 * Mentally draw an attr/char at a given location
501 * Assumes given location and values are valid.
503 void Term_queue_char(int x, int y, byte a, char c, byte ta, char tc)
505 term_win *scrn = Term->scr;
507 byte *scr_aa = &scrn->a[y][x];
508 char *scr_cc = &scrn->c[y][x];
510 byte *scr_taa = &scrn->ta[y][x];
511 char *scr_tcc = &scrn->tc[y][x];
513 /* Hack -- Ignore non-changes */
514 if ((*scr_aa == a) && (*scr_cc == c) &&
515 (*scr_taa == ta) && (*scr_tcc == tc)) return;
517 /* Save the "literal" information */
524 /* Check for new min/max row info */
525 if (y < Term->y1) Term->y1 = y;
526 if (y > Term->y2) Term->y2 = y;
528 /* Check for new min/max col info for this row */
529 if (x < Term->x1[y]) Term->x1[y] = x;
530 if (x > Term->x2[y]) Term->x2[y] = x;
532 if ((scrn->a[y][x] & AF_BIGTILE) == AF_BIGTILE)
533 if ((x - 1) < Term->x1[y]) Term->x1[y]--;
538 * Bigtile version of Term_queue_char().
540 * If use_bigtile is FALSE, simply call Term_queue_char().
542 * Otherwise, mentally draw a pair of attr/char at a given location.
544 * Assumes given location and values are valid.
546 void Term_queue_bigchar(int x, int y, byte a, char c, byte ta, char tc)
551 * A table which relates each ascii character to a multibyte
554 * ¡Ö¢£¡×¤ÏÆóÇÜÉýƦÉå¤ÎÆâÉô¥³¡¼¥É¤Ë»ÈÍÑ¡£
556 static char ascii_to_zenkaku[] =
557 "¡¡¡ª¡É¡ô¡ð¡ó¡õ¡Ç¡Ê¡Ë¡ö¡Ü¡¤¡Ý¡¥¡¿"
558 "£°£±£²£³£´£µ£¶£·£¸£¹¡§¡¨¡ã¡á¡ä¡©"
559 "¡÷£Á£Â£Ã£Ä£Å£Æ£Ç£È£É£Ê£Ë£Ì£Í£Î£Ï"
560 "£Ð£Ñ£Ò£Ó£Ô£Õ£Ö£×£Ø£Ù£Ú¡Î¡À¡Ï¡°¡²"
561 "¡Æ£á£â£ã£ä£å£æ£ç£è£é£ê£ë£ì£í£î£ï"
562 "£ð£ñ£ò£ó£ô£õ£ö£÷£ø£ù£ú¡Ð¡Ã¡Ñ¡Á¢£";
568 /* If non bigtile mode, call orginal function */
571 Term_queue_char(x, y, a, c, ta, tc);
575 /* A tile become a Bigtile */
576 if ((a & 0x80) && (c & 0x80))
578 /* Mark it as a Bigtile */
583 /* Ignore non-tile background */
584 if (!((ta & 0x80) && (tc & 0x80)))
593 * Use a multibyte character instead of a dirty pair of ASCII
596 else if (' ' <= c) /* isprint(c) */
598 c2 = ascii_to_zenkaku[2 * (c - ' ') + 1];
599 c = ascii_to_zenkaku[2 * (c - ' ')];
601 /* Mark it as a Kanji */
609 /* Dirty pair of ASCII characters */
614 /* Display pair of attr/char */
615 Term_queue_char(x, y, a, c, ta, tc);
616 Term_queue_char(x + 1, y, a2, c2, 0, 0);
621 * Mentally draw a string of attr/chars at a given location
623 * Assumes given location and values are valid.
625 * This function is designed to be fast, with no consistancy checking.
626 * It is used to update the map in the game.
628 void Term_queue_line(int x, int y, int n, byte *a, char *c, byte *ta, char *tc)
630 term_win *scrn = Term->scr;
635 byte *scr_aa = &scrn->a[y][x];
636 char *scr_cc = &scrn->c[y][x];
638 byte *scr_taa = &scrn->ta[y][x];
639 char *scr_tcc = &scrn->tc[y][x];
643 /* Hack -- Ignore non-changes */
644 if ((*scr_aa == *a) && (*scr_cc == *c) &&
645 (*scr_taa == *ta) && (*scr_tcc == *tc))
659 /* Save the "literal" information */
663 /* Save the "literal" information */
667 /* Track minimum changed column */
670 /* Track maximum changed column */
676 /* Expand the "change area" as needed */
679 /* Check for new min/max row info */
680 if (y < Term->y1) Term->y1 = y;
681 if (y > Term->y2) Term->y2 = y;
683 /* Check for new min/max col info in this row */
684 if (x1 < Term->x1[y]) Term->x1[y] = x1;
685 if (x2 > Term->x2[y]) Term->x2[y] = x2;
692 * Mentally draw some attr/chars at a given location
694 * Assumes that (x,y) is a valid location, that the first "n" characters
695 * of the string "s" are all valid (non-zero), and that (x+n-1,y) is also
696 * a valid location, so the first "n" characters of "s" can all be added
697 * starting at (x,y) without causing any illegal operations.
699 void Term_queue_chars(int x, int y, int n, byte a, cptr s)
701 int x1 = -1, x2 = -1;
703 byte *scr_aa = Term->scr->a[y];
705 char *scr_cc = Term->scr->c[y];
707 byte *scr_taa = Term->scr->ta[y];
708 char *scr_tcc = Term->scr->tc[y];
710 char *scr_cc = Term->scr->c[y];
712 byte *scr_taa = Term->scr->ta[y];
713 char *scr_tcc = Term->scr->tc[y];
719 if (n == 0 || *s == 0) return;
721 * Á´³Ñʸ»ú¤Î±¦È¾Ê¬¤«¤éʸ»ú¤òɽ¼¨¤¹¤ë¾ì¹ç¡¢
722 * ½Å¤Ê¤Ã¤¿Ê¸»ú¤Îº¸Éôʬ¤ò¾Ãµî¡£
723 * ɽ¼¨³«»Ï°ÌÃÖ¤¬º¸Ã¼¤Ç¤Ê¤¤¤È²¾Äê¡£
725 if ((scr_aa[x] & AF_KANJI2) && (scr_aa[x] & AF_BIGTILE) != AF_BIGTILE)
728 scr_aa[x - 1] &= AF_KANJIC;
732 /* Queue the attr/chars */
733 for ( ; n; x++, s++, n--)
736 /* Æüìʸ»ú¤È¤·¤ÆMSB¤¬Î©¤Ã¤Æ¤¤¤ë²ÄǽÀ¤¬¤¢¤ë */
737 /* ¤½¤Î¾ì¹çattr¤ÎMSB¤âΩ¤Ã¤Æ¤¤¤ë¤Î¤Ç¤³¤ì¤Ç¼±Ê̤¹¤ë */
739 if (!(a & 0x80) && iskanji(*s))
744 byte na1 = (a | AF_KANJI1);
745 byte na2 = (a | AF_KANJI2);
747 if((--n == 0) || !nc2) break;
749 if(scr_aa[x++] == na1 && scr_aa[x] == na2 &&
750 scr_cc[x - 1] == nc1 && scr_cc[x] == nc2 &&
751 (scr_taa[x - 1] == 0) && (scr_taa[x]==0) &&
752 (scr_tcc[x - 1] == 0) && (scr_tcc[x]==0) )
760 if(x1 < 0) x1 = x - 1;
769 byte ota = scr_taa[x];
770 char otc = scr_tcc[x];
772 /* Hack -- Ignore non-changes */
773 if ((oa == a) && (oc == *s) && (ota == 0) && (otc == 0)) continue;
775 /* Save the "literal" information */
782 /* Note the "range" of window updates */
792 * Á´³Ñʸ»ú¤Îº¸È¾Ê¬¤Çɽ¼¨¤ò½ªÎ»¤¹¤ë¾ì¹ç¡¢
793 * ½Å¤Ê¤Ã¤¿Ê¸»ú¤Î±¦Éôʬ¤ò¾Ãµî¡£
794 * (¾ò·ïÄɲ᧥¿¥¤¥ë¤Î1ʸ»úÌܤǤʤ¤»ö¤ò³Î¤«¤á¤ë¤è¤¦¤Ë¡£)
799 Term_get_size(&w, &h);
800 if (x != w && (scr_aa[x] & AF_BIGTILE) != AF_BIGTILE && (scr_aa[x] & AF_KANJI2))
803 scr_aa[x] &= AF_KANJIC;
809 /* Expand the "change area" as needed */
812 /* Check for new min/max row info */
813 if (y < Term->y1) Term->y1 = y;
814 if (y > Term->y2) Term->y2 = y;
816 /* Check for new min/max col info in this row */
817 if (x1 < Term->x1[y]) Term->x1[y] = x1;
818 if (x2 > Term->x2[y]) Term->x2[y] = x2;
824 /*** Refresh routines ***/
828 * Flush a row of the current window (see "Term_fresh")
830 * Display text using "Term_pict()"
832 static void Term_fresh_row_pict(int y, int x1, int x2)
836 byte *old_aa = Term->old->a[y];
837 char *old_cc = Term->old->c[y];
839 byte *scr_aa = Term->scr->a[y];
840 char *scr_cc = Term->scr->c[y];
842 byte *old_taa = Term->old->ta[y];
843 char *old_tcc = Term->old->tc[y];
845 byte *scr_taa = Term->scr->ta[y];
846 char *scr_tcc = Term->scr->tc[y];
868 /* Á´³Ñʸ»ú¤Î£²¥Ð¥¤¥ÈÌܤ«¤É¤¦¤« */
871 /* Scan "modified" columns */
872 for (x = x1; x <= x2; x++)
874 /* See what is currently here */
878 /* See what is desired there */
885 /* Á´³Ñʸ»ú£²¥Ð¥¤¥ÈÌÜ */
892 /* Æüìʸ»ú¤È¤·¤ÆMSB¤¬Î©¤Ã¤Æ¤¤¤ë²ÄǽÀ¤¬¤¢¤ë */
893 /* ¤½¤Î¾ì¹çattr¤ÎMSB¤âΩ¤Ã¤Æ¤¤¤ë¤Î¤Ç¤³¤ì¤Ç¼±Ê̤¹¤ë */
895 kanji = (iskanji(nc) && !(na & 0x80));
904 /* Handle unchanged grids */
906 if ((na == oa) && (nc == oc) && (nta == ota) && (ntc == otc)
907 &&(!kanji || (scr_aa[x + 1] == old_aa[x + 1] &&
908 scr_cc[x + 1] == old_cc[x + 1] &&
909 scr_taa[x + 1] == old_taa[x + 1] &&
910 scr_tcc[x + 1] == old_tcc[x + 1])))
912 if ((na == oa) && (nc == oc) && (nta == ota) && (ntc == otc))
918 /* Draw pending attr/char pairs */
919 (void)((*Term->pict_hook)(fx, y, fn,
920 &scr_aa[fx], &scr_cc[fx],&scr_taa[fx], &scr_tcc[fx]));
927 /* Á´³Ñʸ»ú¤Î»þ¤ÏºÆ³«°ÌÃ֤ϡܣ± */
938 /* Save new contents */
945 /* Restart and Advance */
946 if (fn++ == 0) fx = x;
952 /* Draw pending attr/char pairs */
953 (void)((*Term->pict_hook)(fx, y, fn,
954 &scr_aa[fx], &scr_cc[fx], &scr_taa[fx], &scr_tcc[fx]));
961 * Flush a row of the current window (see "Term_fresh")
963 * Display text using "Term_text()" and "Term_wipe()",
964 * but use "Term_pict()" for high-bit attr/char pairs
966 static void Term_fresh_row_both(int y, int x1, int x2)
970 byte *old_aa = Term->old->a[y];
971 char *old_cc = Term->old->c[y];
973 byte *scr_aa = Term->scr->a[y];
974 char *scr_cc = Term->scr->c[y];
976 byte *old_taa = Term->old->ta[y];
977 char *old_tcc = Term->old->tc[y];
978 byte *scr_taa = Term->scr->ta[y];
979 char *scr_tcc = Term->scr->tc[y];
986 /* The "always_text" flag */
987 int always_text = Term->always_text;
996 byte fa = Term->attr_blank;
1005 /* Á´³Ñʸ»ú¤Î£²¥Ð¥¤¥ÈÌܤ«¤É¤¦¤« */
1008 /* Scan "modified" columns */
1009 for (x = x1; x <= x2; x++)
1011 /* See what is currently here */
1015 /* See what is desired there */
1022 /* Á´³Ñʸ»ú£²¥Ð¥¤¥ÈÌÜ */
1029 /* Æüìʸ»ú¤È¤·¤ÆMSB¤¬Î©¤Ã¤Æ¤¤¤ë²ÄǽÀ¤¬¤¢¤ë */
1030 /* ¤½¤Î¾ì¹çattr¤ÎMSB¤âΩ¤Ã¤Æ¤¤¤ë¤Î¤Ç¤³¤ì¤Ç¼±Ê̤¹¤ë */
1032 /* kanji = (iskanji(nc)); */
1033 kanji = (iskanji(nc) && !(na & 0x80));
1042 /* Handle unchanged grids */
1044 if ((na == oa) && (nc == oc) && (nta == ota) && (ntc == otc)&&
1045 (!kanji || (scr_aa[x + 1] == old_aa[x + 1] &&
1046 scr_cc[x + 1] == old_cc[x + 1] &&
1047 scr_taa[x + 1] == old_taa[x + 1] &&
1048 scr_tcc[x + 1] == old_tcc[x + 1])))
1050 if ((na == oa) && (nc == oc) && (nta == ota) && (ntc == otc))
1056 /* Draw pending chars (normal) */
1057 if (fa || always_text)
1060 send_text_to_chuukei_server(fx, y, fn, fa, &scr_cc[fx]);
1062 (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
1065 /* Draw pending chars (black) */
1069 send_wipe_to_chuukei_server(fx, y, fn);
1071 (void)((*Term->wipe_hook)(fx, y, fn));
1079 /* Á´³Ñʸ»ú¤Î»þ¤ÏºÆ³«°ÌÃ֤ϡܣ± */
1091 /* Save new contents */
1098 /* 2nd byte of bigtile */
1099 if ((na & AF_BIGTILE) == AF_BIGTILE) continue;
1101 /* Handle high-bit attr/chars */
1102 if ((na & 0x80) && (nc & 0x80))
1107 /* Draw pending chars (normal) */
1108 if (fa || always_text)
1111 send_text_to_chuukei_server(fx, y, fn, fa, &scr_cc[fx]);
1113 (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
1116 /* Draw pending chars (black) */
1120 send_wipe_to_chuukei_server(fx, y, fn);
1122 (void)((*Term->wipe_hook)(fx, y, fn));
1129 /* Hack -- Draw the special attr/char pair */
1130 (void)((*Term->pict_hook)(x, y, 1, &na, &nc, &nta, &ntc));
1136 /* Notice new color */
1138 if (fa != (na & AF_KANJIC))
1147 /* Draw the pending chars */
1148 if (fa || always_text)
1151 send_text_to_chuukei_server(fx, y, fn, fa, &scr_cc[fx]);
1153 (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
1156 /* Hack -- Erase "leading" spaces */
1160 send_wipe_to_chuukei_server(fx, y, fn);
1162 (void)((*Term->wipe_hook)(fx, y, fn));
1169 /* Save the new color */
1171 fa = (na & AF_KANJIC);
1178 /* Restart and Advance */
1179 if (fn++ == 0) fx = x;
1185 /* Draw pending chars (normal) */
1186 if (fa || always_text)
1189 send_text_to_chuukei_server(fx, y, fn, fa, &scr_cc[fx]);
1191 (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
1194 /* Draw pending chars (black) */
1198 send_wipe_to_chuukei_server(fx, y, fn);
1200 (void)((*Term->wipe_hook)(fx, y, fn));
1207 * Flush a row of the current window (see "Term_fresh")
1209 * Display text using "Term_text()" and "Term_wipe()"
1211 static void Term_fresh_row_text(int y, int x1, int x2)
1215 byte *old_aa = Term->old->a[y];
1216 char *old_cc = Term->old->c[y];
1218 byte *scr_aa = Term->scr->a[y];
1219 char *scr_cc = Term->scr->c[y];
1221 /* The "always_text" flag */
1222 int always_text = Term->always_text;
1224 /* Pending length */
1231 byte fa = Term->attr_blank;
1240 /* Á´³Ñʸ»ú¤Î£²¥Ð¥¤¥ÈÌܤ«¤É¤¦¤« */
1243 for (x = 0; x < x1; x++)
1244 if (!(old_aa[x] & 0x80) && iskanji(old_cc[x]))
1255 /* Scan "modified" columns */
1256 for (x = x1; x <= x2; x++)
1258 /* See what is currently here */
1262 /* See what is desired there */
1269 /* Á´³Ñʸ»ú£²¥Ð¥¤¥ÈÌÜ */
1276 /* Æüìʸ»ú¤È¤·¤ÆMSB¤¬Î©¤Ã¤Æ¤¤¤ë²ÄǽÀ¤¬¤¢¤ë */
1277 /* ¤½¤Î¾ì¹çattr¤ÎMSB¤âΩ¤Ã¤Æ¤¤¤ë¤Î¤Ç¤³¤ì¤Ç¼±Ê̤¹¤ë */
1279 kanji = (iskanji(nc) && !(na & 0x80));
1281 /* Handle unchanged grids */
1283 if ((na == oa) && (nc == oc) &&
1284 (!kanji || (scr_aa[x + 1] == old_aa[x + 1] &&
1285 scr_cc[x + 1] == old_cc[x + 1])))
1287 if ((na == oa) && (nc == oc))
1294 /* Draw pending chars (normal) */
1295 if (fa || always_text)
1298 send_text_to_chuukei_server(fx, y, fn, fa, &scr_cc[fx]);
1300 (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
1303 /* Draw pending chars (black) */
1307 send_wipe_to_chuukei_server(fx, y, fn);
1309 (void)((*Term->wipe_hook)(fx, y, fn));
1317 /* Á´³Ñʸ»ú¤Î»þ¤ÏºÆ³«°ÌÃ֤ϡܣ± */
1329 /* Save new contents */
1333 /* Notice new color */
1335 if (fa != (na & AF_KANJIC))
1344 /* Draw the pending chars */
1345 if (fa || always_text)
1348 send_text_to_chuukei_server(fx, y, fn, fa, &scr_cc[fx]);
1350 (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
1353 /* Hack -- Erase "leading" spaces */
1357 send_wipe_to_chuukei_server(fx, y, fn);
1359 (void)((*Term->wipe_hook)(fx, y, fn));
1366 /* Save the new color */
1368 fa = (na & AF_KANJIC);
1375 /* Restart and Advance */
1376 if (fn++ == 0) fx = x;
1382 /* Draw pending chars (normal) */
1383 if (fa || always_text)
1386 send_text_to_chuukei_server(fx, y, fn, fa, &scr_cc[fx]);
1388 (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
1391 /* Draw pending chars (black) */
1395 send_wipe_to_chuukei_server(fx, y, fn);
1397 (void)((*Term->wipe_hook)(fx, y, fn));
1407 * Actually perform all requested changes to the window
1409 * If absolutely nothing has changed, not even temporarily, or if the
1410 * current "Term" is not mapped, then this function will return 1 and
1411 * do absolutely nothing.
1413 * Note that when "soft_cursor" is true, we erase the cursor (if needed)
1414 * whenever anything has changed, and redraw it (if needed) after all of
1415 * the screen updates are complete. This will induce a small amount of
1416 * "cursor flicker" but only when the screen has been updated. If the
1417 * screen is updated and then restored, you may still get this flicker.
1419 * When "soft_cursor" is not true, we make the cursor invisible before
1420 * doing anything else if it is supposed to be invisible by the time we
1421 * are done, and we make it visible after moving it to its final location
1422 * after all of the screen updates are complete.
1424 * Note that "Term_xtra(TERM_XTRA_CLEAR,0)" must erase the entire screen,
1425 * including the cursor, if needed, and may place the cursor anywhere.
1427 * Note that "Term_xtra(TERM_XTRA_FROSH,y)" will be always be called
1428 * after any row "y" has been "flushed", unless the "Term->never_frosh"
1429 * flag is set, and "Term_xtra(TERM_XTRA_FRESH,0)" will be called after
1430 * all of the rows have been "flushed".
1432 * Note the use of three different functions to handle the actual flush,
1433 * based on the settings of the "Term->always_pict" and "Term->higher_pict"
1434 * flags (see below).
1436 * The three helper functions (above) work by collecting similar adjacent
1437 * grids into stripes, and then sending each stripe to "Term->pict_hook",
1438 * "Term->text_hook", or "Term->wipe_hook", based on the settings of the
1439 * "Term->always_pict" and "Term->higher_pict" flags, which select which
1440 * of the helper functions to call to flush each row.
1442 * The helper functions currently "skip" any grids which already contain
1443 * the desired contents. This may or may not be the best method, especially
1444 * when the desired content fits nicely into the current stripe. For example,
1445 * it might be better to go ahead and queue them while allowed, but keep a
1446 * count of the "trailing skipables", then, when time to flush, or when a
1447 * "non skippable" is found, force a flush if there are too many skippables.
1449 * Perhaps an "initialization" stage, where the "text" (and "attr")
1450 * buffers are "filled" with information, converting "blanks" into
1451 * a convenient representation, and marking "skips" with "zero chars",
1452 * and then some "processing" is done to determine which chars to skip.
1454 * Currently, the helper functions are optimal for systems which prefer
1455 * to "print a char + move a char + print a char" to "print three chars",
1456 * and for applications that do a lot of "detailed" color printing.
1458 * In the two "queue" functions, total "non-changes" are "pre-skipped".
1459 * The helper functions must also handle situations in which the contents
1460 * of a grid are changed, but then changed back to the original value,
1461 * and situations in which two grids in the same row are changed, but
1462 * the grids between them are unchanged.
1464 * If the "Term->always_pict" flag is set, then "Term_fresh_row_pict()"
1465 * will be used instead of "Term_fresh_row_text()". This allows all the
1466 * modified grids to be collected into stripes of attr/char pairs, which
1467 * are then sent to the "Term->pict_hook" hook, which can draw these pairs
1468 * in whatever way it would like.
1470 * If the "Term->higher_pict" flag is set, then "Term_fresh_row_both()"
1471 * will be used instead of "Term_fresh_row_text()". This allows all the
1472 * "special" attr/char pairs (in which both the attr and char have the
1473 * high-bit set) to be sent (one pair at a time) to the "Term->pict_hook"
1474 * hook, which can draw these pairs in whatever way it would like.
1476 * Normally, the "Term_wipe()" function is used only to display "blanks"
1477 * that were induced by "Term_clear()" or "Term_erase()", and then only
1478 * if the "attr_blank" and "char_blank" fields have not been redefined
1479 * to use "white space" instead of the default "black space". Actually,
1480 * the "Term_wipe()" function is used to display all "black" text, such
1481 * as the default "spaces" created by "Term_clear()" and "Term_erase()".
1483 * Note that the "Term->always_text" flag will disable the use of the
1484 * "Term_wipe()" function hook entirely, and force all text, even text
1485 * drawn in the color "black", to be explicitly drawn. This is useful
1486 * for machines which implement "Term_wipe()" by just drawing spaces.
1488 * Note that the "Term->always_pict" flag will disable the use of the
1489 * "Term_wipe()" function entirely, and force everything, even text
1490 * drawn in the attr "black", to be explicitly drawn.
1492 * Note that if no "black" text is ever drawn, and if "attr_blank" is
1493 * not "zero", then the "Term_wipe" hook will never be used, even if
1494 * the "Term->always_text" flag is not set.
1496 * This function does nothing unless the "Term" is "mapped", which allows
1497 * certain systems to optimize the handling of "closed" windows.
1499 * On systems with a "soft" cursor, we must explicitly erase the cursor
1500 * before flushing the output, if needed, to prevent a "jumpy" refresh.
1501 * The actual method for this is horrible, but there is very little that
1502 * we can do to simplify it efficiently. XXX XXX XXX
1504 * On systems with a "hard" cursor, we will "hide" the cursor before
1505 * flushing the output, if needed, to avoid a "flickery" refresh. It
1506 * would be nice to *always* hide the cursor during the refresh, but
1507 * this might be expensive (and/or ugly) on some machines.
1509 * The "Term->icky_corner" flag is used to avoid calling "Term_wipe()"
1510 * or "Term_pict()" or "Term_text()" on the bottom right corner of the
1511 * window, which might induce "scrolling" or other nasty stuff on old
1512 * dumb terminals. This flag is handled very efficiently. We assume
1513 * that the "Term_curs()" call will prevent placing the cursor in the
1514 * corner, if needed, though I doubt such placement is ever a problem.
1515 * Currently, the use of "Term->icky_corner" and "Term->soft_cursor"
1516 * together may result in undefined behavior.
1518 errr Term_fresh(void)
1528 term_win *old = Term->old;
1529 term_win *scr = Term->scr;
1532 /* Do nothing unless "mapped" */
1533 if (!Term->mapped_flag) return (1);
1536 /* Trivial Refresh */
1538 (scr->cu == old->cu) &&
1539 (scr->cv == old->cv) &&
1540 (scr->cx == old->cx) &&
1541 (scr->cy == old->cy) &&
1542 !(Term->total_erase))
1549 /* Paranoia -- use "fake" hooks to prevent core dumps */
1550 if (!Term->curs_hook) Term->curs_hook = Term_curs_hack;
1551 if (!Term->bigcurs_hook) Term->bigcurs_hook = Term->curs_hook;
1552 if (!Term->wipe_hook) Term->wipe_hook = Term_wipe_hack;
1553 if (!Term->text_hook) Term->text_hook = Term_text_hack;
1554 if (!Term->pict_hook) Term->pict_hook = Term_pict_hack;
1557 /* Handle "total erase" */
1558 if (Term->total_erase)
1560 byte na = Term->attr_blank;
1561 char nc = Term->char_blank;
1563 /* Physically erase the entire window */
1564 Term_xtra(TERM_XTRA_CLEAR, 0);
1566 /* Hack -- clear all "cursor" data */
1567 old->cv = old->cu = old->cx = old->cy = 0;
1570 for (y = 0; y < h; y++)
1572 byte *aa = old->a[y];
1573 char *cc = old->c[y];
1575 byte *taa = old->ta[y];
1576 char *tcc = old->tc[y];
1579 /* Wipe each column */
1580 for (x = 0; x < w; x++)
1582 /* Wipe each grid */
1591 /* Redraw every row */
1593 Term->y2 = y2 = h - 1;
1595 /* Redraw every column */
1596 for (y = 0; y < h; y++)
1599 Term->x2[y] = w - 1;
1602 /* Forget "total erase" */
1603 Term->total_erase = FALSE;
1607 /* Cursor update -- Erase old Cursor */
1608 if (Term->soft_cursor)
1610 /* Cursor was visible */
1611 if (!old->cu && old->cv)
1617 byte *old_aa = old->a[ty];
1618 char *old_cc = old->c[ty];
1620 byte *old_taa = old->ta[ty];
1621 char *old_tcc = old->tc[ty];
1623 byte ota = old_taa[tx];
1624 char otc = old_tcc[tx];
1627 if (tx + 1 < Term->wid && !(old_aa[tx] & 0x80)
1628 && iskanji(old_cc[tx]))
1631 /* Hack -- use "Term_pict()" always */
1632 if (Term->always_pict)
1634 (void)((*Term->pict_hook)(tx, ty, csize, &old_aa[tx], &old_cc[tx], &ota, &otc));
1637 /* Hack -- use "Term_pict()" sometimes */
1638 else if (Term->higher_pict && (old_aa[tx] & 0x80) && (old_cc[tx] & 0x80))
1640 (void)((*Term->pict_hook)(tx, ty, 1, &old_aa[tx], &old_cc[tx], &ota, &otc));
1643 /* Hack -- restore the actual character */
1644 else if (old_aa[tx] || Term->always_text)
1648 send_text_to_chuukei_server(tx, ty, csize, (old_aa[tx] & 0xf), &old_cc[tx]);
1650 (void)((*Term->text_hook)(tx, ty, csize, (unsigned char) (old_aa[tx] & 0xf), &old_cc[tx]));
1653 /* Hack -- erase the grid */
1657 send_wipe_to_chuukei_server(tx, ty, 1);
1659 (void)((*Term->wipe_hook)(tx, ty, 1));
1664 /* Cursor Update -- Erase old Cursor */
1667 /* Cursor will be invisible */
1668 if (scr->cu || !scr->cv)
1670 /* Make the cursor invisible */
1671 Term_xtra(TERM_XTRA_SHAPE, 0);
1676 /* Something to update */
1679 /* Handle "icky corner" */
1680 if (Term->icky_corner)
1682 /* Avoid the corner */
1685 /* Avoid the corner */
1686 if (Term->x2[h - 1] > w - 2)
1688 /* Avoid the corner */
1689 Term->x2[h - 1] = w - 2;
1695 /* Scan the "modified" rows */
1696 for (y = y1; y <= y2; ++y)
1698 int x1 = Term->x1[y];
1699 int x2 = Term->x2[y];
1701 /* Flush each "modified" row */
1704 /* Always use "Term_pict()" */
1705 if (Term->always_pict)
1708 Term_fresh_row_pict(y, x1, x2);
1711 /* Sometimes use "Term_pict()" */
1712 else if (Term->higher_pict)
1715 Term_fresh_row_both(y, x1, x2);
1718 /* Never use "Term_pict()" */
1722 Term_fresh_row_text(y, x1, x2);
1725 /* This row is all done */
1729 /* Hack -- Flush that row (if allowed) */
1730 if (!Term->never_frosh) Term_xtra(TERM_XTRA_FROSH, y);
1734 /* No rows are invalid */
1740 /* Cursor update -- Show new Cursor */
1741 if (Term->soft_cursor)
1743 /* Draw the cursor */
1744 if (!scr->cu && scr->cv)
1747 send_curs_to_chuukei_server(scr->cx, scr->cy);
1751 if ((scr->cx + 1 < w) &&
1752 ((old->a[scr->cy][scr->cx + 1] & AF_BIGTILE) == AF_BIGTILE ||
1753 (!(old->a[scr->cy][scr->cx] & 0x80) &&
1754 iskanji(old->c[scr->cy][scr->cx]))))
1756 if ((scr->cx + 1 < w) && (old->a[scr->cy][scr->cx + 1] & AF_BIGTILE) == AF_BIGTILE)
1759 /* Double width cursor for the Bigtile mode */
1760 (void)((*Term->bigcurs_hook)(scr->cx, scr->cy));
1764 /* Call the cursor display routine */
1765 (void)((*Term->curs_hook)(scr->cx, scr->cy));
1770 /* Cursor Update -- Show new Cursor */
1773 /* The cursor is useless, hide it */
1777 send_curs_to_chuukei_server(w - 1, scr->cy);
1779 /* Paranoia -- Put the cursor NEAR where it belongs */
1780 (void)((*Term->curs_hook)(w - 1, scr->cy));
1782 /* Make the cursor invisible */
1783 /* Term_xtra(TERM_XTRA_SHAPE, 0); */
1786 /* The cursor is invisible, hide it */
1790 send_curs_to_chuukei_server(scr->cx, scr->cy);
1792 /* Paranoia -- Put the cursor where it belongs */
1793 (void)((*Term->curs_hook)(scr->cx, scr->cy));
1795 /* Make the cursor invisible */
1796 /* Term_xtra(TERM_XTRA_SHAPE, 0); */
1799 /* The cursor is visible, display it correctly */
1803 send_curs_to_chuukei_server(scr->cx, scr->cy);
1805 /* Put the cursor where it belongs */
1806 (void)((*Term->curs_hook)(scr->cx, scr->cy));
1808 /* Make the cursor visible */
1809 Term_xtra(TERM_XTRA_SHAPE, 1);
1814 /* Save the "cursor state" */
1821 /* Actually flush the output */
1822 Term_xtra(TERM_XTRA_FRESH, 0);
1831 /*** Output routines ***/
1835 * Set the cursor visibility
1837 errr Term_set_cursor(int v)
1840 if (Term->scr->cv == v) return (1);
1851 * Place the cursor at a given location
1853 * Note -- "illegal" requests do not move the cursor.
1855 errr Term_gotoxy(int x, int y)
1861 if ((x < 0) || (x >= w)) return (-1);
1862 if ((y < 0) || (y >= h)) return (-1);
1864 /* Remember the cursor */
1868 /* The cursor is not useless */
1877 * At a given location, place an attr/char
1878 * Do not change the cursor position
1879 * No visual changes until "Term_fresh()".
1881 errr Term_draw(int x, int y, byte a, char c)
1886 /* Verify location */
1887 if ((x < 0) || (x >= w)) return (-1);
1888 if ((y < 0) || (y >= h)) return (-1);
1890 /* Paranoia -- illegal char */
1891 if (!c) return (-2);
1893 /* Queue it for later */
1894 Term_queue_char(x, y, a, c, 0, 0);
1902 * Using the given attr, add the given char at the cursor.
1904 * We return "-2" if the character is "illegal". XXX XXX
1906 * We return "-1" if the cursor is currently unusable.
1908 * We queue the given attr/char for display at the current
1909 * cursor location, and advance the cursor to the right,
1910 * marking it as unuable and returning "1" if it leaves
1911 * the screen, and otherwise returning "0".
1913 * So when this function, or the following one, return a
1914 * positive value, future calls to either function will
1915 * return negative ones.
1917 errr Term_addch(byte a, char c)
1921 /* Handle "unusable" cursor */
1922 if (Term->scr->cu) return (-1);
1924 /* Paranoia -- no illegal chars */
1925 if (!c) return (-2);
1927 /* Queue the given character for display */
1928 Term_queue_char(Term->scr->cx, Term->scr->cy, a, c, 0, 0);
1930 /* Advance the cursor */
1934 if (Term->scr->cx < w) return (0);
1936 /* Note "Useless" cursor */
1939 /* Note "Useless" cursor */
1945 * At the current location, using an attr, add a string
1947 * We also take a length "n", using negative values to imply
1948 * the largest possible value, and then we use the minimum of
1949 * this length and the "actual" length of the string as the
1950 * actual number of characters to attempt to display, never
1951 * displaying more characters than will actually fit, since
1952 * we do NOT attempt to "wrap" the cursor at the screen edge.
1954 * We return "-1" if the cursor is currently unusable.
1955 * We return "N" if we were "only" able to write "N" chars,
1956 * even if all of the given characters fit on the screen,
1957 * and mark the cursor as unusable for future attempts.
1959 * So when this function, or the preceding one, return a
1960 * positive value, future calls to either function will
1961 * return negative ones.
1963 errr Term_addstr(int n, byte a, cptr s)
1971 /* Handle "unusable" cursor */
1972 if (Term->scr->cu) return (-1);
1974 /* Obtain maximal length */
1975 k = (n < 0) ? (w + 1) : n;
1977 /* Obtain the usable string length */
1978 for (n = 0; (n < k) && s[n]; n++) /* loop */;
1980 /* React to reaching the edge of the screen */
1981 if (Term->scr->cx + n >= w) res = n = w - Term->scr->cx;
1983 /* Queue the first "n" characters for display */
1984 Term_queue_chars(Term->scr->cx, Term->scr->cy, n, a, s);
1986 /* Advance the cursor */
1989 /* Hack -- Notice "Useless" cursor */
1990 if (res) Term->scr->cu = 1;
1992 /* Success (usually) */
1998 * Move to a location and, using an attr, add a char
2000 errr Term_putch(int x, int y, byte a, char c)
2005 if ((res = Term_gotoxy(x, y)) != 0) return (res);
2007 /* Then add the char */
2008 if ((res = Term_addch(a, c)) != 0) return (res);
2016 * Move to a location and, using an attr, add a string
2018 errr Term_putstr(int x, int y, int n, byte a, cptr s)
2023 if ((res = Term_gotoxy(x, y)) != 0) return (res);
2025 /* Then add the string */
2026 if ((res = Term_addstr(n, a, s)) != 0) return (res);
2034 * Move to a location and, using an attr, add a string vertically
2036 errr Term_putstr_v(int x, int y, int n, byte a, cptr s)
2043 for (i = 0; i < n && s[i] != 0; i++)
2046 if ((res = Term_gotoxy(x, y0)) != 0) return (res);
2050 if ((res = Term_addstr(2, a, &s[i])) != 0) return (res);
2053 if (s[i] == 0) break;
2055 if ((res = Term_addstr(1, a, &s[i])) != 0) return (res);
2066 * Place cursor at (x,y), and clear the next "n" chars
2068 errr Term_erase(int x, int y, int n)
2073 /* int h = Term->hgt; */
2078 int na = Term->attr_blank;
2079 int nc = Term->char_blank;
2088 if (Term_gotoxy(x, y)) return (-1);
2090 /* Force legal size */
2091 if (x + n > w) n = w - x;
2094 scr_aa = Term->scr->a[y];
2095 scr_cc = Term->scr->c[y];
2097 scr_taa = Term->scr->ta[y];
2098 scr_tcc = Term->scr->tc[y];
2102 * Á´³Ñʸ»ú¤Î±¦È¾Ê¬¤«¤éʸ»ú¤òɽ¼¨¤¹¤ë¾ì¹ç¡¢
2103 * ½Å¤Ê¤Ã¤¿Ê¸»ú¤Îº¸Éôʬ¤ò¾Ãµî¡£
2105 if (n > 0 && (((scr_aa[x] & AF_KANJI2) && (scr_aa[x] & AF_BIGTILE) != AF_BIGTILE)
2106 || (scr_aa[x] & AF_BIGTILE) == AF_BIGTILE))
2108 if (n > 0 && (scr_aa[x] & AF_BIGTILE) == AF_BIGTILE)
2115 /* Scan every column */
2116 for (i = 0; i < n; i++, x++)
2121 /* Hack -- Ignore "non-changes" */
2122 if ((oa == na) && (oc == nc)) continue;
2126 * Á´³Ñʸ»ú¤Îº¸È¾Ê¬¤Çɽ¼¨¤ò½ªÎ»¤¹¤ë¾ì¹ç¡¢
2127 * ½Å¤Ê¤Ã¤¿Ê¸»ú¤Î±¦Éôʬ¤ò¾Ãµî¡£
2129 * 2001/04/29 -- Habu
2130 * ¹Ô¤Î±¦Ã¼¤Î¾ì¹ç¤Ï¤³¤Î½èÍý¤ò¤·¤Ê¤¤¤è¤¦¤Ë½¤Àµ¡£
2132 if ((oa & AF_KANJI1) && (i + 1) == n && x != w - 1)
2135 /* Save the "literal" information */
2142 /* Track minimum changed column */
2145 /* Track maximum changed column */
2149 /* Expand the "change area" as needed */
2152 /* Check for new min/max row info */
2153 if (y < Term->y1) Term->y1 = y;
2154 if (y > Term->y2) Term->y2 = y;
2156 /* Check for new min/max col info in this row */
2157 if (x1 < Term->x1[y]) Term->x1[y] = x1;
2158 if (x2 > Term->x2[y]) Term->x2[y] = x2;
2167 * Clear the entire window, and move to the top left corner
2169 * Note the use of the special "total_erase" code
2171 errr Term_clear(void)
2178 byte na = Term->attr_blank;
2179 char nc = Term->char_blank;
2184 /* Cursor to the top left */
2185 Term->scr->cx = Term->scr->cy = 0;
2188 for (y = 0; y < h; y++)
2190 byte *scr_aa = Term->scr->a[y];
2191 char *scr_cc = Term->scr->c[y];
2193 byte *scr_taa = Term->scr->ta[y];
2194 char *scr_tcc = Term->scr->tc[y];
2196 /* Wipe each column */
2197 for (x = 0; x < w; x++)
2206 /* This row has changed */
2208 Term->x2[y] = w - 1;
2211 /* Every row has changed */
2215 /* Force "total erase" */
2216 Term->total_erase = TRUE;
2227 * Redraw (and refresh) the whole window.
2229 errr Term_redraw(void)
2231 /* Force "total erase" */
2232 Term->total_erase = TRUE;
2234 /* Hack -- Refresh */
2243 * Redraw part of a widow.
2245 errr Term_redraw_section(int x1, int y1, int x2, int y2)
2251 /* Bounds checking */
2252 if (y2 >= Term->hgt) y2 = Term->hgt - 1;
2253 if (x2 >= Term->wid) x2 = Term->wid - 1;
2261 /* Set the x limits */
2262 for (i = Term->y1; i <= Term->y2; i++)
2270 if (Term->scr->a[i][x1j] & AF_KANJI2) x1j--;
2273 if (x2j < Term->wid - 1)
2275 if (Term->scr->a[i][x2j] & AF_KANJI1) x2j++;
2281 c_ptr = Term->old->c[i];
2283 /* Clear the section so it is redrawn */
2284 for (j = x1j; j <= x2j; j++)
2286 /* Hack - set the old character to "none" */
2293 c_ptr = Term->old->c[i];
2295 /* Clear the section so it is redrawn */
2296 for (j = x1; j <= x2; j++)
2298 /* Hack - set the old character to "none" */
2304 /* Hack -- Refresh */
2313 /*** Access routines ***/
2317 * Extract the cursor visibility
2319 errr Term_get_cursor(int *v)
2321 /* Extract visibility */
2322 (*v) = Term->scr->cv;
2330 * Extract the current window size
2332 errr Term_get_size(int *w, int *h)
2334 /* Access the cursor */
2344 * Extract the current cursor location
2346 errr Term_locate(int *x, int *y)
2348 /* Access the cursor */
2349 (*x) = Term->scr->cx;
2350 (*y) = Term->scr->cy;
2352 /* Warn about "useless" cursor */
2353 if (Term->scr->cu) return (1);
2361 * At a given location, determine the "current" attr and char
2362 * Note that this refers to what will be on the window after the
2363 * next call to "Term_fresh()". It may or may not already be there.
2365 errr Term_what(int x, int y, byte *a, char *c)
2370 /* Verify location */
2371 if ((x < 0) || (x >= w)) return (-1);
2372 if ((y < 0) || (y >= h)) return (-1);
2375 (*a) = Term->scr->a[y][x];
2376 (*c) = Term->scr->c[y][x];
2384 /*** Input routines ***/
2388 * Flush and forget the input
2390 errr Term_flush(void)
2392 /* Hack -- Flush all events */
2393 Term_xtra(TERM_XTRA_FLUSH, 0);
2395 /* Forget all keypresses */
2396 Term->key_head = Term->key_tail = 0;
2405 * Add a keypress to the "queue"
2407 errr Term_keypress(int k)
2409 /* Hack -- Refuse to enqueue non-keys */
2410 if (!k) return (-1);
2412 /* Store the char, advance the queue */
2413 Term->key_queue[Term->key_head++] = k;
2415 /* Circular queue, handle wrap */
2416 if (Term->key_head == Term->key_size) Term->key_head = 0;
2418 /* Success (unless overflow) */
2419 if (Term->key_head != Term->key_tail) return (0);
2422 /* Hack -- Forget the oldest key */
2423 if (++Term->key_tail == Term->key_size) Term->key_tail = 0;
2432 * Add a keypress to the FRONT of the "queue"
2434 errr Term_key_push(int k)
2436 /* Hack -- Refuse to enqueue non-keys */
2437 if (!k) return (-1);
2439 /* Hack -- Overflow may induce circular queue */
2440 if (Term->key_tail == 0) Term->key_tail = Term->key_size;
2442 /* Back up, Store the char */
2443 Term->key_queue[--Term->key_tail] = k;
2445 /* Success (unless overflow) */
2446 if (Term->key_head != Term->key_tail) return (0);
2449 /* Hack -- Forget the oldest key */
2450 if (++Term->key_tail == Term->key_size) Term->key_tail = 0;
2462 * Check for a pending keypress on the key queue.
2464 * Store the keypress, if any, in "ch", and return "0".
2465 * Otherwise store "zero" in "ch", and return "1".
2467 * Wait for a keypress if "wait" is true.
2469 * Remove the keypress if "take" is true.
2471 errr Term_inkey(char *ch, bool wait, bool take)
2480 /* Hack -- get bored */
2481 if (!Term->never_bored)
2483 /* Process random events */
2484 Term_xtra(TERM_XTRA_BORED, 0);
2490 /* Process pending events while necessary */
2491 while (Term->key_head == Term->key_tail)
2493 /* Process events (wait for one) */
2494 Term_xtra(TERM_XTRA_EVENT, TRUE);
2501 /* Process pending events if necessary */
2502 if (Term->key_head == Term->key_tail)
2504 /* Process events (do not wait) */
2505 Term_xtra(TERM_XTRA_EVENT, FALSE);
2509 /* No keys are ready */
2510 if (Term->key_head == Term->key_tail) return (1);
2512 /* Extract the next keypress */
2513 (*ch) = Term->key_queue[Term->key_tail];
2515 /* If requested, advance the queue, wrap around if necessary */
2516 if (take && (++Term->key_tail == Term->key_size)) Term->key_tail = 0;
2524 /*** Extra routines ***/
2528 * Save the "requested" screen into the "memorized" screen
2530 * Every "Term_save()" should match exactly one "Term_load()"
2532 errr Term_save(void)
2540 /* Allocate window */
2541 MAKE(Term->mem, term_win);
2543 /* Initialize window */
2544 term_win_init(Term->mem, w, h);
2548 term_win_copy(Term->mem, Term->scr, w, h);
2556 * Restore the "requested" contents (see above).
2558 * Every "Term_save()" should match exactly one "Term_load()"
2560 errr Term_load(void)
2570 /* Allocate window */
2571 MAKE(Term->mem, term_win);
2573 /* Initialize window */
2574 term_win_init(Term->mem, w, h);
2578 term_win_copy(Term->scr, Term->mem, w, h);
2581 for (y = 0; y < h; y++)
2585 Term->x2[y] = w - 1;
2598 * Exchange the "requested" screen with the "tmp" screen
2600 errr Term_exchange(void)
2607 term_win *exchanger;
2613 /* Allocate window */
2614 MAKE(Term->tmp, term_win);
2616 /* Initialize window */
2617 term_win_init(Term->tmp, w, h);
2621 exchanger = Term->scr;
2622 Term->scr = Term->tmp;
2623 Term->tmp = exchanger;
2626 for (y = 0; y < h; y++)
2630 Term->x2[y] = w - 1;
2644 * React to a new physical window size.
2646 errr Term_resize(int w, int h)
2660 /* Resizing is forbidden */
2661 if (Term->fixed_shape) return (-1);
2663 /* Ignore illegal changes */
2664 if ((w < 1) || (h < 1)) return (-1);
2667 /* Ignore non-changes */
2668 if ((Term->wid == w) && (Term->hgt == h) && (arg_bigtile == use_bigtile))
2671 use_bigtile = arg_bigtile;
2673 /* Minimum dimensions */
2674 wid = MIN(Term->wid, w);
2675 hgt = MIN(Term->hgt, h);
2681 /* Save old window */
2682 hold_old = Term->old;
2684 /* Save old window */
2685 hold_scr = Term->scr;
2687 /* Save old window */
2688 hold_mem = Term->mem;
2690 /* Save old window */
2691 hold_tmp = Term->tmp;
2693 /* Create new scanners */
2694 C_MAKE(Term->x1, h, byte);
2695 C_MAKE(Term->x2, h, byte);
2697 /* Create new window */
2698 MAKE(Term->old, term_win);
2700 /* Initialize new window */
2701 term_win_init(Term->old, w, h);
2703 /* Save the contents */
2704 term_win_copy(Term->old, hold_old, wid, hgt);
2706 /* Create new window */
2707 MAKE(Term->scr, term_win);
2709 /* Initialize new window */
2710 term_win_init(Term->scr, w, h);
2712 /* Save the contents */
2713 term_win_copy(Term->scr, hold_scr, wid, hgt);
2718 /* Create new window */
2719 MAKE(Term->mem, term_win);
2721 /* Initialize new window */
2722 term_win_init(Term->mem, w, h);
2724 /* Save the contents */
2725 term_win_copy(Term->mem, hold_mem, wid, hgt);
2731 /* Create new window */
2732 MAKE(Term->tmp, term_win);
2734 /* Initialize new window */
2735 term_win_init(Term->tmp, w, h);
2737 /* Save the contents */
2738 term_win_copy(Term->tmp, hold_tmp, wid, hgt);
2741 /* Free some arrays */
2742 C_KILL(hold_x1, Term->hgt, byte);
2743 C_KILL(hold_x2, Term->hgt, byte);
2746 term_win_nuke(hold_old, Term->wid, Term->hgt);
2749 KILL(hold_old, term_win);
2751 /* Illegal cursor */
2752 if (Term->old->cx >= w) Term->old->cu = 1;
2753 if (Term->old->cy >= h) Term->old->cu = 1;
2756 term_win_nuke(hold_scr, Term->wid, Term->hgt);
2759 KILL(hold_scr, term_win);
2761 /* Illegal cursor */
2762 if (Term->scr->cx >= w) Term->scr->cu = 1;
2763 if (Term->scr->cy >= h) Term->scr->cu = 1;
2769 term_win_nuke(hold_mem, Term->wid, Term->hgt);
2772 KILL(hold_mem, term_win);
2774 /* Illegal cursor */
2775 if (Term->mem->cx >= w) Term->mem->cu = 1;
2776 if (Term->mem->cy >= h) Term->mem->cu = 1;
2783 term_win_nuke(hold_tmp, Term->wid, Term->hgt);
2786 KILL(hold_tmp, term_win);
2788 /* Illegal cursor */
2789 if (Term->tmp->cx >= w) Term->tmp->cu = 1;
2790 if (Term->tmp->cy >= h) Term->tmp->cu = 1;
2797 /* Force "total erase" */
2798 Term->total_erase = TRUE;
2801 for (i = 0; i < h; i++)
2805 Term->x2[i] = w - 1;
2812 /* Execute the "resize_hook" hook, if available */
2813 if (Term->resize_hook)
2815 Term->resize_hook();
2825 * Activate a new Term (and deactivate the current Term)
2827 * This function is extremely important, and also somewhat bizarre.
2828 * It is the only function that should "modify" the value of "Term".
2830 * To "create" a valid "term", one should do "term_init(t)", then
2831 * set the various flags and hooks, and then do "Term_activate(t)".
2833 errr Term_activate(term *t)
2835 /* Hack -- already done */
2836 if (Term == t) return (1);
2838 /* Deactivate the old Term */
2839 if (Term) Term_xtra(TERM_XTRA_LEVEL, 0);
2841 /* Hack -- Call the special "init" hook */
2842 if (t && !t->active_flag)
2844 /* Call the "init" hook */
2845 if (t->init_hook) (*t->init_hook)(t);
2848 t->active_flag = TRUE;
2851 t->mapped_flag = TRUE;
2854 /* Remember the Term */
2857 /* Activate the new Term */
2858 if (Term) Term_xtra(TERM_XTRA_LEVEL, 1);
2869 errr term_nuke(term *t)
2875 /* Hack -- Call the special "nuke" hook */
2878 /* Call the "nuke" hook */
2879 if (t->nuke_hook) (*t->nuke_hook)(t);
2882 t->active_flag = FALSE;
2884 /* Assume not mapped */
2885 t->mapped_flag = FALSE;
2889 /* Nuke "displayed" */
2890 term_win_nuke(t->old, w, h);
2892 /* Kill "displayed" */
2893 KILL(t->old, term_win);
2895 /* Nuke "requested" */
2896 term_win_nuke(t->scr, w, h);
2898 /* Kill "requested" */
2899 KILL(t->scr, term_win);
2904 /* Nuke "memorized" */
2905 term_win_nuke(t->mem, w, h);
2907 /* Kill "memorized" */
2908 KILL(t->mem, term_win);
2914 /* Nuke "temporary" */
2915 term_win_nuke(t->tmp, w, h);
2917 /* Kill "temporary" */
2918 KILL(t->tmp, term_win);
2921 /* Free some arrays */
2922 C_KILL(t->x1, h, byte);
2923 C_KILL(t->x2, h, byte);
2925 /* Free the input queue */
2926 C_KILL(t->key_queue, t->key_size, char);
2934 * Initialize a term, using a window of the given size.
2935 * Also prepare the "input queue" for "k" keypresses
2936 * By default, the cursor starts out "invisible"
2937 * By default, we "erase" using "black spaces"
2939 errr term_init(term *t, int w, int h, int k)
2945 (void)WIPE(t, term);
2948 /* Prepare the input queue */
2949 t->key_head = t->key_tail = 0;
2951 /* Determine the input queue size */
2954 /* Allocate the input queue */
2955 C_MAKE(t->key_queue, t->key_size, char);
2962 /* Allocate change arrays */
2963 C_MAKE(t->x1, h, byte);
2964 C_MAKE(t->x2, h, byte);
2967 /* Allocate "displayed" */
2968 MAKE(t->old, term_win);
2970 /* Initialize "displayed" */
2971 term_win_init(t->old, w, h);
2974 /* Allocate "requested" */
2975 MAKE(t->scr, term_win);
2977 /* Initialize "requested" */
2978 term_win_init(t->scr, w, h);
2982 for (y = 0; y < h; y++)
2993 /* Force "total erase" */
2994 t->total_erase = TRUE;
2997 /* Default "blank" */
2999 t->char_blank = ' ';