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- */
25 * °À¤ËÁ´³Ñʸ»ú¤Î£±¥Ð¥¤¥ÈÌÜ¡¢£²¥Ð¥¤¥ÈÌܤ⵲±¡£
30 * This file provides a generic, efficient, terminal window package,
31 * which can be used not only on standard terminal environments such
32 * as dumb terminals connected to a Unix box, but also in more modern
33 * "graphic" environments, such as the Macintosh or Unix/X11.
35 * Each "window" works like a standard "dumb terminal", that is, it
36 * can display a two dimensional array of grids containing colored
37 * textual symbols, plus an optional cursor, and it can be used to
38 * get keypress events from the user.
40 * In fact, this package can simply be used, if desired, to support
41 * programs which will look the same on a dumb terminal as they do
42 * on a graphic platform such as the Macintosh.
44 * This package was designed to help port the game "Angband" to a wide
45 * variety of different platforms. Angband, like many other games in
46 * the "rogue-like" heirarchy, requires, at the minimum, the ability
47 * to display "colored textual symbols" in a standard 80x24 "window",
48 * such as that provided by most dumb terminals, and many old personal
49 * computers, and to check for "keypresses" from the user. The major
50 * concerns were thus portability and efficiency, so Angband could be
51 * easily ported to many different systems, with minimal effort, and
52 * yet would run quickly on each of these systems, no matter what kind
53 * of underlying hardware/software support was being used.
55 * It is important to understand the differences between the older
56 * "dumb terminals" and the newer "graphic interface" machines, since
57 * this package was designed to work with both types of systems.
60 * waiting for a keypress is complex
61 * checking for a keypress is often cheap
62 * changing "colors" may be expensive
63 * the "color" of a "blank" is rarely important
64 * moving the "cursor" is relatively cheap
65 * use a "software" cursor (only moves when requested)
66 * drawing characters normally will not erase old ones
67 * drawing a character on the cursor often erases it
68 * may have fast routines for "clear a region"
69 * the bottom right corner is usually not special
72 * waiting for a keypress is simple
73 * checking for a keypress is often expensive
74 * changing "colors" is usually cheap
75 * the "color" of a "blank" may be important
76 * moving the "cursor" may be expensive
77 * use a "hardware" cursor (moves during screen updates)
78 * drawing new symbols automatically erases old ones
79 * characters may only be drawn at the cursor location
80 * drawing a character on the cursor will move the cursor
81 * may have fast routines for "clear entire window"
82 * may have fast routines for "clear to end of line"
83 * the bottom right corner is often dangerous
86 * This package provides support for multiple windows, each of an
87 * arbitrary size (up to 255x255), each with its own set of flags,
88 * and its own hooks to handle several low-level procedures which
89 * differ from platform to platform. Then the main program simply
90 * creates one or more "term" structures, setting the various flags
91 * and hooks in a manner appropriate for the current platform, and
92 * then it can use the various "term" structures without worrying
93 * about the underlying platform.
96 * This package allows each "grid" in each window to hold an attr/char
97 * pair, with each ranging from 0 to 255, and makes very few assumptions
98 * about the meaning of any attr/char values. Normally, we assume that
99 * "attr 0" is "black", with the semantics that "black" text should be
100 * sent to "Term_wipe()" instead of "Term_text()", but this sematics is
101 * modified if either the "always_pict" or the "always_text" flags are
102 * set. We assume that "char 0" is "dangerous", since placing such a
103 * "char" in the middle of a string "terminates" the string, and usually
104 * we prevent its use.
106 * Finally, we use a special attr/char pair, defaulting to "attr 0" and
107 * "char 32", also known as "black space", when we "erase" or "clear"
108 * any window, but this pair can be redefined to any pair, including
109 * the standard "white space", or the bizarre "emptiness" ("attr 0"
110 * and "char 0"), as long as various obscure restrictions are met.
113 * This package provides several functions which allow a program to
114 * interact with the "term" structures. Most of the functions allow
115 * the program to "request" certain changes to the current "term",
116 * such as moving the cursor, drawing an attr/char pair, erasing a
117 * region of grids, hiding the cursor, etc. Then there is a special
118 * function which causes all of the "pending" requests to be performed
119 * in an efficient manner. There is another set of functions which
120 * allow the program to query the "requested state" of the current
121 * "term", such as asking for the cursor location, or what attr/char
122 * is at a given location, etc. There is another set of functions
123 * dealing with "keypress" events, which allows the program to ask if
124 * the user has pressed any keys, or to forget any keys the user pressed.
125 * There is a pair of functions to allow this package to memorize the
126 * contents of the current "term", and to restore these contents at
127 * a later time. There is a special function which allows the program
128 * to specify which "term" structure should be the "current" one. At
129 * the lowest level, there is a set of functions which allow a new
130 * "term" to be initialized or destroyed, and which allow this package,
131 * or a program, to access the special "hooks" defined for the current
132 * "term", and a set of functions which those "hooks" can use to inform
133 * this package of the results of certain occurances, for example, one
134 * such function allows this package to learn about user keypresses,
135 * detected by one of the special "hooks".
137 * We provide, among other things, the functions "Term_keypress()"
138 * to "react" to keypress events, and "Term_redraw()" to redraw the
139 * entire window, plus "Term_resize()" to note a new size.
142 * Note that the current "term" contains two "window images". One of
143 * these images represents the "requested" contents of the "term", and
144 * the other represents the "actual" contents of the "term", at the time
145 * of the last performance of pending requests. This package uses these
146 * two images to determine the "minimal" amount of work needed to make
147 * the "actual" contents of the "term" match the "requested" contents of
148 * the "term". This method is not perfect, but it often reduces the
149 * amount of work needed to perform the pending requests, which thus
150 * increases the speed of the program itself. This package promises
151 * that the requested changes will appear to occur either "all at once"
152 * or in a "top to bottom" order. In addition, a "cursor" is maintained,
153 * and this cursor is updated along with the actual window contents.
155 * Currently, the "Term_fresh()" routine attempts to perform the "minimum"
156 * number of physical updates, in terms of total "work" done by the hooks
157 * Term_wipe(), Term_text(), and Term_pict(), making use of the fact that
158 * adjacent characters of the same color can both be drawn together using
159 * the "Term_text()" hook, and that "black" text can often be sent to the
160 * "Term_wipe()" hook instead of the "Term_text()" hook, and if something
161 * is already displayed in a window, then it is not necessary to display
162 * it again. Unfortunately, this may induce slightly non-optimal results
163 * in some cases, in particular, those in which, say, a string of ten
164 * characters needs to be written, but the fifth character has already
165 * been displayed. Currently, this will cause the "Term_text()" routine
166 * to be called once for each half of the string, instead of once for the
167 * whole string, which, on some machines, may be non-optimal behavior.
169 * The new formalism includes a "displayed" screen image (old) which
170 * is actually seen by the user, a "requested" screen image (scr)
171 * which is being prepared for display, a "memorized" screen image
172 * (mem) which is used to save and restore screen images, and a
173 * "temporary" screen image (tmp) which is currently unused.
176 * Several "flags" are available in each "term" to allow the underlying
177 * visual system (which initializes the "term" structure) to "optimize"
178 * the performance of this package for the given system, or to request
179 * certain behavior which is helpful/required for the given system.
181 * The "soft_cursor" flag indicates the use of a "soft" cursor, which
182 * only moves when explicitly requested,and which is "erased" when
183 * any characters are drawn on top of it. This flag is used for all
184 * "graphic" systems which handle the cursor by "drawing" it.
186 * The "icky_corner" flag indicates that the bottom right "corner"
187 * of the windows are "icky", and "printing" anything there may
188 * induce "messy" behavior, such as "scrolling". This flag is used
189 * for most old "dumb terminal" systems.
192 * The "term" structure contains the following function "hooks":
194 * Term->init_hook = Init the term
195 * Term->nuke_hook = Nuke the term
196 * Term->user_hook = Perform user actions
197 * Term->xtra_hook = Perform extra actions
198 * Term->curs_hook = Draw (or Move) the cursor
199 * Term->wipe_hook = Draw some blank spaces
200 * Term->text_hook = Draw some text in the window
201 * Term->pict_hook = Draw some attr/chars in the window
203 * The "Term->user_hook" hook provides a simple hook to an implementation
204 * defined function, with application defined semantics. It is available
205 * to the program via the "Term_user()" function.
207 * The "Term->xtra_hook" hook provides a variety of different functions,
208 * based on the first parameter (which should be taken from the various
209 * TERM_XTRA_* defines) and the second parameter (which may make sense
210 * only for some first parameters). It is available to the program via
211 * the "Term_xtra()" function, though some first parameters are only
212 * "legal" when called from inside this package.
214 * The "Term->curs_hook" hook provides this package with a simple way
215 * to "move" or "draw" the cursor to the grid "x,y", depending on the
216 * setting of the "soft_cursor" flag. Note that the cursor is never
217 * redrawn if "nothing" has happened to the screen (even temporarily).
218 * This hook is required.
220 * The "Term->wipe_hook" hook provides this package with a simple way
221 * to "erase", starting at "x,y", the next "n" grids. This hook assumes
222 * that the input is valid. This hook is required, unless the setting
223 * of the "always_pict" or "always_text" flags makes it optional.
225 * The "Term->text_hook" hook provides this package with a simple way
226 * to "draw", starting at "x,y", the "n" chars contained in "cp", using
227 * the attr "a". This hook assumes that the input is valid, and that
228 * "n" is between 1 and 256 inclusive, but it should NOT assume that
229 * the contents of "cp" are null-terminated. This hook is required,
230 * unless the setting of the "always_pict" flag makes it optional.
232 * The "Term->pict_hook" hook provides this package with a simple way
233 * to "draw", starting at "x,y", the "n" attr/char pairs contained in
234 * the arrays "ap" and "cp". This hook assumes that the input is valid,
235 * and that "n" is between 1 and 256 inclusive, but it should NOT assume
236 * that the contents of "cp" are null-terminated. This hook is optional,
237 * unless the setting of the "always_pict" or "higher_pict" flags make
238 * it required. Note that recently, this hook was changed from taking
239 * a byte "a" and a char "c" to taking a length "n", an array of bytes
240 * "ap" and an array of chars "cp". Old implementations of this hook
241 * should now iterate over all "n" attr/char pairs.
244 * The game "Angband" uses a set of files called "main-xxx.c", for
245 * various "xxx" suffixes. Most of these contain a function called
246 * "init_xxx()", that will prepare the underlying visual system for
247 * use with Angband, and then create one or more "term" structures,
248 * using flags and hooks appropriate to the given platform, so that
249 * the "main()" function can call one (or more) of the "init_xxx()"
250 * functions, as appropriate, to prepare the required "term" structs
251 * (one for each desired sub-window), and these "init_xxx()" functions
252 * are called from a centralized "main()" function in "main.c". Other
253 * "main-xxx.c" systems contain their own "main()" function which, in
254 * addition to doing everything needed to initialize the actual program,
255 * also does everything that the normal "init_xxx()" functions would do.
257 * The game "Angband" defines, in addition to "attr 0", all of the
258 * attr codes from 1 to 15, using definitions in "defines.h", and
259 * thus the "main-xxx.c" files used by Angband must handle these
260 * attr values correctly. Also, they must handle all other attr
261 * values, though they may do so in any way they wish, for example,
262 * by always taking every attr code mod 16. Many of the "main-xxx.c"
263 * files use "white space" ("attr 1" / "char 32") to "erase" or "clear"
264 * any window, for efficiency.
266 * The game "Angband" uses the "Term_user" hook to allow any of the
267 * "main-xxx.c" files to interact with the user, by calling this hook
268 * whenever the user presses the "!" key when the game is waiting for
269 * a new command. This could be used, for example, to provide "unix
270 * shell commands" to the Unix versions of the game.
272 * See "main-xxx.c" for a simple skeleton file which can be used to
273 * create a "visual system" for a new platform when porting Angband.
289 /*** Local routines ***/
293 * Nuke a term_win (see below)
295 static errr term_win_nuke(term_win *s, int w, int h)
297 /* Free the window access arrays */
298 C_KILL(s->a, h, byte*);
299 C_KILL(s->c, h, char*);
301 /* Free the window content arrays */
302 C_KILL(s->va, h * w, byte);
303 C_KILL(s->vc, h * w, char);
305 #ifdef USE_TRANSPARENCY
307 /* Free the terrain access arrays */
308 C_KILL(s->ta, h, byte*);
309 C_KILL(s->tc, h, char*);
311 /* Free the terrain content arrays */
312 C_KILL(s->vta, h * w, byte);
313 C_KILL(s->vtc, h * w, char);
315 #endif /* USE_TRANSPARENCY */
323 * Initialize a "term_win" (using the given window size)
325 static errr term_win_init(term_win *s, int w, int h)
329 /* Make the window access arrays */
330 C_MAKE(s->a, h, byte*);
331 C_MAKE(s->c, h, char*);
333 /* Make the window content arrays */
334 C_MAKE(s->va, h * w, byte);
335 C_MAKE(s->vc, h * w, char);
337 #ifdef USE_TRANSPARENCY
339 /* Make the terrain access arrays */
340 C_MAKE(s->ta, h, byte*);
341 C_MAKE(s->tc, h, char*);
343 /* Make the terrain content arrays */
344 C_MAKE(s->vta, h * w, byte);
345 C_MAKE(s->vtc, h * w, char);
347 #endif /* USE_TRANSPARENCY */
350 /* Prepare the window access arrays */
351 for (y = 0; y < h; y++)
353 s->a[y] = s->va + w * y;
354 s->c[y] = s->vc + w * y;
356 #ifdef USE_TRANSPARENCY
358 s->ta[y] = s->vta + w * y;
359 s->tc[y] = s->vtc + w * y;
361 #endif /* USE_TRANSPARENCY */
371 * Copy a "term_win" from another
373 static errr term_win_copy(term_win *s, term_win *f, int w, int h)
378 for (y = 0; y < h; y++)
380 byte *f_aa = f->a[y];
381 char *f_cc = f->c[y];
383 byte *s_aa = s->a[y];
384 char *s_cc = s->c[y];
386 #ifdef USE_TRANSPARENCY
388 byte *f_taa = f->ta[y];
389 char *f_tcc = f->tc[y];
391 byte *s_taa = s->ta[y];
392 char *s_tcc = s->tc[y];
394 #endif /* USE_TRANSPARENCY */
396 for (x = 0; x < w; x++)
401 #ifdef USE_TRANSPARENCY
404 #endif /* USE_TRANSPARENCY */
420 /*** External hooks ***/
424 * Execute the "Term->user_hook" hook, if available (see above).
426 errr Term_user(int n)
428 /* Verify the hook */
429 if (!Term->user_hook) return (-1);
432 return ((*Term->user_hook)(n));
436 * Execute the "Term->xtra_hook" hook, if available (see above).
438 errr Term_xtra(int n, int v)
440 /* Verify the hook */
441 if (!Term->xtra_hook) return (-1);
444 if( n == TERM_XTRA_CLEAR || n == TERM_XTRA_FRESH || n == TERM_XTRA_SHAPE )
445 send_xtra_to_chuukei_server(n);
448 return ((*Term->xtra_hook)(n, v));
457 * Hack -- fake hook for "Term_curs()" (see above)
459 static errr Term_curs_hack(int x, int y)
461 /* Compiler silliness */
462 if (x || y) return (-2);
469 * Hack -- fake hook for "Term_wipe()" (see above)
471 static errr Term_wipe_hack(int x, int y, int n)
473 /* Compiler silliness */
474 if (x || y || n) return (-2);
481 * Hack -- fake hook for "Term_text()" (see above)
484 static errr Term_text_hack(int x, int y, int n, byte a, cptr cp)
486 static errr Term_text_hack(int x, int y, int n, byte a, const char *cp)
490 /* Compiler silliness */
491 if (x || y || n || a || cp) return (-2);
498 * Hack -- fake hook for "Term_pict()" (see above)
500 #ifdef USE_TRANSPARENCY
501 static errr Term_pict_hack(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp)
502 #else /* USE_TRANSPARENCY */
503 static errr Term_pict_hack(int x, int y, int n, const byte *ap, const char *cp)
504 #endif /* USE_TRANSPARENCY */
506 /* Compiler silliness */
507 #ifdef USE_TRANSPARENCY
508 if (x || y || n || ap || cp || tap || tcp) return (-2);
509 #else /* USE_TRANSPARENCY */
510 if (x || y || n || ap || cp) return (-2);
511 #endif /* USE_TRANSPARENCY */
519 /*** Efficient routines ***/
523 * Mentally draw an attr/char at a given location
525 * Assumes given location and values are valid.
527 #ifdef USE_TRANSPARENCY
528 void Term_queue_char(int x, int y, byte a, char c, byte ta, char tc)
529 #else /* USE_TRANSPARENCY */
530 void Term_queue_char(int x, int y, byte a, char c)
531 #endif /* USE_TRANSPARENCY */
533 term_win *scrn = Term->scr;
535 byte *scr_aa = &scrn->a[y][x];
536 char *scr_cc = &scrn->c[y][x];
538 #ifdef USE_TRANSPARENCY
540 byte *scr_taa = &scrn->ta[y][x];
541 char *scr_tcc = &scrn->tc[y][x];
543 /* Hack -- Ignore non-changes */
544 if ((*scr_aa == a) && (*scr_cc == c) &&
545 (*scr_taa == ta) && (*scr_tcc == tc)) return;
547 #else /* USE_TRANSPARENCY */
549 /* Hack -- Ignore non-changes */
550 if ((*scr_aa == a) && (*scr_cc == c)) return;
552 #endif /* USE_TRANSPARENCY */
554 /* Save the "literal" information */
558 #ifdef USE_TRANSPARENCY
563 #endif /* USE_TRANSPARENCY */
565 /* Check for new min/max row info */
566 if (y < Term->y1) Term->y1 = y;
567 if (y > Term->y2) Term->y2 = y;
569 /* Check for new min/max col info for this row */
570 if (x < Term->x1[y]) Term->x1[y] = x;
571 if (x > Term->x2[y]) Term->x2[y] = x;
576 * Mentally draw a string of attr/chars at a given location
578 * Assumes given location and values are valid.
580 * This function is designed to be fast, with no consistancy checking.
581 * It is used to update the map in the game.
583 #ifdef USE_TRANSPARENCY
584 void Term_queue_line(int x, int y, int n, byte *a, char *c, byte *ta, char *tc)
585 #else /* USE_TRANSPARENCY */
586 void Term_queue_line(int x, int y, int n, byte *a, char *c)
587 #endif /* USE_TRANSPARENCY */
589 term_win *scrn = Term->scr;
594 byte *scr_aa = &scrn->a[y][x];
595 char *scr_cc = &scrn->c[y][x];
597 #ifdef USE_TRANSPARENCY
599 byte *scr_taa = &scrn->ta[y][x];
600 char *scr_tcc = &scrn->tc[y][x];
602 #endif /* USE_TRANSPARENCY */
607 #ifdef USE_TRANSPARENCY
609 /* Hack -- Ignore non-changes */
610 if ((*scr_aa == *a) && (*scr_cc == *c) &&
611 (*scr_taa == *ta) && (*scr_tcc == *tc))
625 /* Save the "literal" information */
629 #else /* USE_TRANSPARENCY */
631 /* Hack -- Ignore non-changes */
632 if ((*scr_aa == *a) && (*scr_cc == *c))
642 #endif /* USE_TRANSPARENCY */
644 /* Save the "literal" information */
648 /* Track minimum changed column */
651 /* Track maximum changed column */
657 /* Expand the "change area" as needed */
660 /* Check for new min/max row info */
661 if (y < Term->y1) Term->y1 = y;
662 if (y > Term->y2) Term->y2 = y;
664 /* Check for new min/max col info in this row */
665 if (x1 < Term->x1[y]) Term->x1[y] = x1;
666 if (x2 > Term->x2[y]) Term->x2[y] = x2;
673 * Mentally draw some attr/chars at a given location
675 * Assumes that (x,y) is a valid location, that the first "n" characters
676 * of the string "s" are all valid (non-zero), and that (x+n-1,y) is also
677 * a valid location, so the first "n" characters of "s" can all be added
678 * starting at (x,y) without causing any illegal operations.
680 void Term_queue_chars(int x, int y, int n, byte a, cptr s)
682 int x1 = -1, x2 = -1;
684 byte *scr_aa = Term->scr->a[y];
686 unsigned char *scr_cc = (unsigned char *)Term->scr->c[y];
688 #ifdef USE_TRANSPARENCY
689 byte *scr_taa = Term->scr->ta[y];
690 unsigned char *scr_tcc = Term->scr->tc[y];
691 #endif /* USE_TRANSPARENCY */
694 char *scr_cc = Term->scr->c[y];
696 #ifdef USE_TRANSPARENCY
698 byte *scr_taa = Term->scr->ta[y];
699 char *scr_tcc = Term->scr->tc[y];
701 #endif /* USE_TRANSPARENCY */
707 if (n == 0 || *s == 0) return;
709 * Á´³Ñʸ»ú¤Î±¦È¾Ê¬¤«¤éʸ»ú¤òɽ¼¨¤¹¤ë¾ì¹ç¡¢
710 * ½Å¤Ê¤Ã¤¿Ê¸»ú¤Îº¸Éôʬ¤ò¾Ãµî¡£
711 * ɽ¼¨³«»Ï°ÌÃÖ¤¬º¸Ã¼¤Ç¤Ê¤¤¤È²¾Äê¡£
713 if (scr_aa[x] & KANJI2)
716 scr_aa[x - 1] &= KANJIC;
720 /* Queue the attr/chars */
721 for ( ; n; x++, s++, n--)
724 /* Æüìʸ»ú¤È¤·¤ÆMSB¤¬Î©¤Ã¤Æ¤¤¤ë²ÄǽÀ¤¬¤¢¤ë */
725 /* ¤½¤Î¾ì¹çattr¤ÎMSB¤âΩ¤Ã¤Æ¤¤¤ë¤Î¤Ç¤³¤ì¤Ç¼±Ê̤¹¤ë */
727 if (iskanji(*s) && !(a & 0x80))
732 int na1 = (a | KANJI1);
733 int na2 = (a | KANJI2);
735 if((--n == 0) || !nc2) break;
736 #ifdef USE_TRANSPARENCY
737 if(scr_aa[x++] == na1 && scr_aa[x] == na2 &&
738 scr_cc[x - 1] == nc1 && scr_cc[x] == nc2 &&
739 (scr_taa[x - 1] == 0) && (scr_taa[x]==0) &&
740 (scr_tcc[x - 1] == 0) && (scr_tcc[x]==0) )
743 if(scr_aa[x++] == na1 && scr_aa[x] == na2 &&
744 scr_cc[x - 1] == nc1 && scr_cc[x] == nc2) continue;
751 if(x1 < 0) x1 = x - 1;
760 #ifdef USE_TRANSPARENCY
762 int ota = scr_taa[x];
763 int otc = scr_tcc[x];
765 /* Hack -- Ignore non-changes */
766 if ((oa == a) && (oc == *s) && (ota == 0) && (otc == 0)) continue;
768 #else /* USE_TRANSPARENCY */
770 /* Hack -- Ignore non-changes */
771 if ((oa == a) && (oc == *s)) continue;
773 #endif /* USE_TRANSPARENCY */
775 /* Save the "literal" information */
779 #ifdef USE_TRANSPARENCY
784 #endif /* USE_TRANSPARENCY */
786 /* Note the "range" of window updates */
796 * Á´³Ñʸ»ú¤Îº¸È¾Ê¬¤Çɽ¼¨¤ò½ªÎ»¤¹¤ë¾ì¹ç¡¢
797 * ½Å¤Ê¤Ã¤¿Ê¸»ú¤Î±¦Éôʬ¤ò¾Ãµî¡£
798 * (¾ò·ïÄɲ᧥¿¥¤¥ë¤Î1ʸ»úÌܤǤʤ¤»ö¤ò³Î¤«¤á¤ë¤è¤¦¤Ë¡£)
803 Term_get_size(&w, &h);
804 if (x != w && !(scr_aa[x] & 0x80) && (scr_aa[x] & KANJI2))
813 /* Expand the "change area" as needed */
816 /* Check for new min/max row info */
817 if (y < Term->y1) Term->y1 = y;
818 if (y > Term->y2) Term->y2 = y;
820 /* Check for new min/max col info in this row */
821 if (x1 < Term->x1[y]) Term->x1[y] = x1;
822 if (x2 > Term->x2[y]) Term->x2[y] = x2;
828 /*** Refresh routines ***/
832 * Flush a row of the current window (see "Term_fresh")
834 * Display text using "Term_pict()"
836 static void Term_fresh_row_pict(int y, int x1, int x2)
840 byte *old_aa = Term->old->a[y];
841 char *old_cc = Term->old->c[y];
843 byte *scr_aa = Term->scr->a[y];
844 char *scr_cc = Term->scr->c[y];
846 #ifdef USE_TRANSPARENCY
848 byte *old_taa = Term->old->ta[y];
849 char *old_tcc = Term->old->tc[y];
851 byte *scr_taa = Term->scr->ta[y];
852 char *scr_tcc = Term->scr->tc[y];
860 #endif /* USE_TRANSPARENCY */
876 /* Á´³Ñʸ»ú¤Î£²¥Ð¥¤¥ÈÌܤ«¤É¤¦¤« */
879 /* Scan "modified" columns */
880 for (x = x1; x <= x2; x++)
882 /* See what is currently here */
886 /* See what is desired there */
893 /* Á´³Ñʸ»ú£²¥Ð¥¤¥ÈÌÜ */
900 /* Æüìʸ»ú¤È¤·¤ÆMSB¤¬Î©¤Ã¤Æ¤¤¤ë²ÄǽÀ¤¬¤¢¤ë */
901 /* ¤½¤Î¾ì¹çattr¤ÎMSB¤âΩ¤Ã¤Æ¤¤¤ë¤Î¤Ç¤³¤ì¤Ç¼±Ê̤¹¤ë */
903 kanji = (iskanji(nc) && !(na & 0x80));
905 #ifdef USE_TRANSPARENCY
913 /* Handle unchanged grids */
915 if ((na == oa) && (nc == oc) && (nta == ota) && (ntc == otc)
916 &&(!kanji || (scr_aa[x + 1] == old_aa[x + 1] &&
917 scr_cc[x + 1] == old_cc[x + 1] &&
918 scr_taa[x + 1] == old_taa[x + 1] &&
919 scr_tcc[x + 1] == old_tcc[x + 1])))
921 if ((na == oa) && (nc == oc) && (nta == ota) && (ntc == otc))
925 #else /* USE_TRANSPARENCY */
927 /* Handle unchanged grids */
929 if ((na == oa) && (nc == oc) &&
930 (!kanji || (scr_aa[x + 1] == old_aa[x + 1] &&
931 scr_cc[x + 1] == old_cc[x + 1])))
933 if ((na == oa) && (nc == oc))
937 #endif /* USE_TRANSPARENCY */
942 /* Draw pending attr/char pairs */
943 #ifdef USE_TRANSPARENCY
944 (void)((*Term->pict_hook)(fx, y, fn,
945 &scr_aa[fx], &scr_cc[fx],&scr_taa[fx], &scr_tcc[fx]));
946 #else /* USE_TRANSPARENCY */
947 (void)((*Term->pict_hook)(fx, y, fn, &scr_aa[fx], &scr_cc[fx]));
948 #endif /* USE_TRANSPARENCY */
955 /* Á´³Ñʸ»ú¤Î»þ¤ÏºÆ³«°ÌÃ֤ϡܣ± */
966 /* Save new contents */
970 #ifdef USE_TRANSPARENCY
973 #endif /* USE_TRANSPARENCY */
975 /* Restart and Advance */
976 if (fn++ == 0) fx = x;
982 /* Draw pending attr/char pairs */
983 #ifdef USE_TRANSPARENCY
984 (void)((*Term->pict_hook)(fx, y, fn,
985 &scr_aa[fx], &scr_cc[fx], &scr_taa[fx], &scr_tcc[fx]));
986 #else /* USE_TRANSPARENCY */
987 (void)((*Term->pict_hook)(fx, y, fn, &scr_aa[fx], &scr_cc[fx]));
988 #endif /* USE_TRANSPARENCY */
995 * Flush a row of the current window (see "Term_fresh")
997 * Display text using "Term_text()" and "Term_wipe()",
998 * but use "Term_pict()" for high-bit attr/char pairs
1000 static void Term_fresh_row_both(int y, int x1, int x2)
1004 byte *old_aa = Term->old->a[y];
1005 char *old_cc = Term->old->c[y];
1007 byte *scr_aa = Term->scr->a[y];
1008 char *scr_cc = Term->scr->c[y];
1010 #ifdef USE_TRANSPARENCY
1011 byte *old_taa = Term->old->ta[y];
1012 char *old_tcc = Term->old->tc[y];
1013 byte *scr_taa = Term->scr->ta[y];
1014 char *scr_tcc = Term->scr->tc[y];
1020 #endif /* USE_TRANSPARENCY */
1022 /* The "always_text" flag */
1023 int always_text = Term->always_text;
1025 /* Pending length */
1032 byte fa = Term->attr_blank;
1041 /* Á´³Ñʸ»ú¤Î£²¥Ð¥¤¥ÈÌܤ«¤É¤¦¤« */
1044 /* Scan "modified" columns */
1045 for (x = x1; x <= x2; x++)
1047 /* See what is currently here */
1051 /* See what is desired there */
1058 /* Á´³Ñʸ»ú£²¥Ð¥¤¥ÈÌÜ */
1065 /* Æüìʸ»ú¤È¤·¤ÆMSB¤¬Î©¤Ã¤Æ¤¤¤ë²ÄǽÀ¤¬¤¢¤ë */
1066 /* ¤½¤Î¾ì¹çattr¤ÎMSB¤âΩ¤Ã¤Æ¤¤¤ë¤Î¤Ç¤³¤ì¤Ç¼±Ê̤¹¤ë */
1068 /* kanji = (iskanji(nc)); */
1069 kanji = (iskanji(nc) && !(na & 0x80));
1071 #ifdef USE_TRANSPARENCY
1079 /* Handle unchanged grids */
1081 if ((na == oa) && (nc == oc) && (nta == ota) && (ntc == otc)&&
1082 (!kanji || (scr_aa[x + 1] == old_aa[x + 1] &&
1083 scr_cc[x + 1] == old_cc[x + 1] &&
1084 scr_taa[x + 1] == old_taa[x + 1] &&
1085 scr_tcc[x + 1] == old_tcc[x + 1])))
1087 if ((na == oa) && (nc == oc) && (nta == ota) && (ntc == otc))
1091 #else /* USE_TRANSPARENCY */
1093 /* Handle unchanged grids */
1095 if ((na == oa) && (nc == oc) &&
1096 (!kanji || (scr_aa[x + 1] == old_aa[x + 1] &&
1097 scr_cc[x + 1] == old_cc[x + 1])))
1099 if ((na == oa) && (nc == oc))
1103 #endif /* USE_TRANSPARENCY */
1109 /* Draw pending chars (normal) */
1110 if (fa || always_text)
1113 send_text_to_chuukei_server(fx, y, fn, fa, &scr_cc[fx]);
1115 (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
1118 /* Draw pending chars (black) */
1122 send_wipe_to_chuukei_server(fx, y, fn);
1124 (void)((*Term->wipe_hook)(fx, y, fn));
1132 /* Á´³Ñʸ»ú¤Î»þ¤ÏºÆ³«°ÌÃ֤ϡܣ± */
1144 /* Save new contents */
1148 #ifdef USE_TRANSPARENCY
1153 #endif /* USE_TRANSPARENCY */
1155 /* 2nd byte of bigtile */
1156 if (na == 255) continue;
1158 /* Handle high-bit attr/chars */
1159 if ((na & 0x80) && (nc & 0x80))
1164 /* Draw pending chars (normal) */
1165 if (fa || always_text)
1168 send_text_to_chuukei_server(fx, y, fn, fa, &scr_cc[fx]);
1170 (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
1173 /* Draw pending chars (black) */
1177 send_wipe_to_chuukei_server(fx, y, fn);
1179 (void)((*Term->wipe_hook)(fx, y, fn));
1186 #ifdef USE_TRANSPARENCY
1188 /* Hack -- Draw the special attr/char pair */
1189 (void)((*Term->pict_hook)(x, y, 1, &na, &nc, &nta, &ntc));
1191 #else /* USE_TRANSPARENCY */
1193 /* Hack -- Draw the special attr/char pair */
1194 (void)((*Term->pict_hook)(x, y, 1, &na, &nc));
1196 #endif /* USE_TRANSPARENCY */
1202 /* Notice new color */
1204 if (fa != (na & KANJIC))
1213 /* Draw the pending chars */
1214 if (fa || always_text)
1217 send_text_to_chuukei_server(fx, y, fn, fa, &scr_cc[fx]);
1219 (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
1222 /* Hack -- Erase "leading" spaces */
1226 send_wipe_to_chuukei_server(fx, y, fn);
1228 (void)((*Term->wipe_hook)(fx, y, fn));
1235 /* Save the new color */
1244 /* Restart and Advance */
1245 if (fn++ == 0) fx = x;
1251 /* Draw pending chars (normal) */
1252 if (fa || always_text)
1255 send_text_to_chuukei_server(fx, y, fn, fa, &scr_cc[fx]);
1257 (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
1260 /* Draw pending chars (black) */
1264 send_wipe_to_chuukei_server(fx, y, fn);
1266 (void)((*Term->wipe_hook)(fx, y, fn));
1273 * Flush a row of the current window (see "Term_fresh")
1275 * Display text using "Term_text()" and "Term_wipe()"
1277 static void Term_fresh_row_text(int y, int x1, int x2)
1281 byte *old_aa = Term->old->a[y];
1282 char *old_cc = Term->old->c[y];
1284 byte *scr_aa = Term->scr->a[y];
1285 char *scr_cc = Term->scr->c[y];
1287 /* The "always_text" flag */
1288 int always_text = Term->always_text;
1290 /* Pending length */
1297 byte fa = Term->attr_blank;
1306 /* Á´³Ñʸ»ú¤Î£²¥Ð¥¤¥ÈÌܤ«¤É¤¦¤« */
1309 for (x = 0; x < x1; x++)
1310 if (!(old_aa[x] & 0x80) && iskanji(old_cc[x]))
1321 /* Scan "modified" columns */
1322 for (x = x1; x <= x2; x++)
1324 /* See what is currently here */
1328 /* See what is desired there */
1335 /* Á´³Ñʸ»ú£²¥Ð¥¤¥ÈÌÜ */
1342 /* Æüìʸ»ú¤È¤·¤ÆMSB¤¬Î©¤Ã¤Æ¤¤¤ë²ÄǽÀ¤¬¤¢¤ë */
1343 /* ¤½¤Î¾ì¹çattr¤ÎMSB¤âΩ¤Ã¤Æ¤¤¤ë¤Î¤Ç¤³¤ì¤Ç¼±Ê̤¹¤ë */
1345 kanji = (iskanji(nc) && !(na & 0x80));
1347 /* Handle unchanged grids */
1349 if ((na == oa) && (nc == oc) &&
1350 (!kanji || (scr_aa[x + 1] == old_aa[x + 1] &&
1351 scr_cc[x + 1] == old_cc[x + 1])))
1353 if ((na == oa) && (nc == oc))
1360 /* Draw pending chars (normal) */
1361 if (fa || always_text)
1364 send_text_to_chuukei_server(fx, y, fn, fa, &scr_cc[fx]);
1366 (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
1369 /* Draw pending chars (black) */
1373 send_wipe_to_chuukei_server(fx, y, fn);
1375 (void)((*Term->wipe_hook)(fx, y, fn));
1383 /* Á´³Ñʸ»ú¤Î»þ¤ÏºÆ³«°ÌÃ֤ϡܣ± */
1395 /* Save new contents */
1399 /* Notice new color */
1401 if (fa != (na & KANJIC))
1410 /* Draw the pending chars */
1411 if (fa || always_text)
1414 send_text_to_chuukei_server(fx, y, fn, fa, &scr_cc[fx]);
1416 (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
1419 /* Hack -- Erase "leading" spaces */
1423 send_wipe_to_chuukei_server(fx, y, fn);
1425 (void)((*Term->wipe_hook)(fx, y, fn));
1432 /* Save the new color */
1441 /* Restart and Advance */
1442 if (fn++ == 0) fx = x;
1448 /* Draw pending chars (normal) */
1449 if (fa || always_text)
1452 send_text_to_chuukei_server(fx, y, fn, fa, &scr_cc[fx]);
1454 (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
1457 /* Draw pending chars (black) */
1461 send_wipe_to_chuukei_server(fx, y, fn);
1463 (void)((*Term->wipe_hook)(fx, y, fn));
1473 * Actually perform all requested changes to the window
1475 * If absolutely nothing has changed, not even temporarily, or if the
1476 * current "Term" is not mapped, then this function will return 1 and
1477 * do absolutely nothing.
1479 * Note that when "soft_cursor" is true, we erase the cursor (if needed)
1480 * whenever anything has changed, and redraw it (if needed) after all of
1481 * the screen updates are complete. This will induce a small amount of
1482 * "cursor flicker" but only when the screen has been updated. If the
1483 * screen is updated and then restored, you may still get this flicker.
1485 * When "soft_cursor" is not true, we make the cursor invisible before
1486 * doing anything else if it is supposed to be invisible by the time we
1487 * are done, and we make it visible after moving it to its final location
1488 * after all of the screen updates are complete.
1490 * Note that "Term_xtra(TERM_XTRA_CLEAR,0)" must erase the entire screen,
1491 * including the cursor, if needed, and may place the cursor anywhere.
1493 * Note that "Term_xtra(TERM_XTRA_FROSH,y)" will be always be called
1494 * after any row "y" has been "flushed", unless the "Term->never_frosh"
1495 * flag is set, and "Term_xtra(TERM_XTRA_FRESH,0)" will be called after
1496 * all of the rows have been "flushed".
1498 * Note the use of three different functions to handle the actual flush,
1499 * based on the settings of the "Term->always_pict" and "Term->higher_pict"
1500 * flags (see below).
1502 * The three helper functions (above) work by collecting similar adjacent
1503 * grids into stripes, and then sending each stripe to "Term->pict_hook",
1504 * "Term->text_hook", or "Term->wipe_hook", based on the settings of the
1505 * "Term->always_pict" and "Term->higher_pict" flags, which select which
1506 * of the helper functions to call to flush each row.
1508 * The helper functions currently "skip" any grids which already contain
1509 * the desired contents. This may or may not be the best method, especially
1510 * when the desired content fits nicely into the current stripe. For example,
1511 * it might be better to go ahead and queue them while allowed, but keep a
1512 * count of the "trailing skipables", then, when time to flush, or when a
1513 * "non skippable" is found, force a flush if there are too many skippables.
1515 * Perhaps an "initialization" stage, where the "text" (and "attr")
1516 * buffers are "filled" with information, converting "blanks" into
1517 * a convenient representation, and marking "skips" with "zero chars",
1518 * and then some "processing" is done to determine which chars to skip.
1520 * Currently, the helper functions are optimal for systems which prefer
1521 * to "print a char + move a char + print a char" to "print three chars",
1522 * and for applications that do a lot of "detailed" color printing.
1524 * In the two "queue" functions, total "non-changes" are "pre-skipped".
1525 * The helper functions must also handle situations in which the contents
1526 * of a grid are changed, but then changed back to the original value,
1527 * and situations in which two grids in the same row are changed, but
1528 * the grids between them are unchanged.
1530 * If the "Term->always_pict" flag is set, then "Term_fresh_row_pict()"
1531 * will be used instead of "Term_fresh_row_text()". This allows all the
1532 * modified grids to be collected into stripes of attr/char pairs, which
1533 * are then sent to the "Term->pict_hook" hook, which can draw these pairs
1534 * in whatever way it would like.
1536 * If the "Term->higher_pict" flag is set, then "Term_fresh_row_both()"
1537 * will be used instead of "Term_fresh_row_text()". This allows all the
1538 * "special" attr/char pairs (in which both the attr and char have the
1539 * high-bit set) to be sent (one pair at a time) to the "Term->pict_hook"
1540 * hook, which can draw these pairs in whatever way it would like.
1542 * Normally, the "Term_wipe()" function is used only to display "blanks"
1543 * that were induced by "Term_clear()" or "Term_erase()", and then only
1544 * if the "attr_blank" and "char_blank" fields have not been redefined
1545 * to use "white space" instead of the default "black space". Actually,
1546 * the "Term_wipe()" function is used to display all "black" text, such
1547 * as the default "spaces" created by "Term_clear()" and "Term_erase()".
1549 * Note that the "Term->always_text" flag will disable the use of the
1550 * "Term_wipe()" function hook entirely, and force all text, even text
1551 * drawn in the color "black", to be explicitly drawn. This is useful
1552 * for machines which implement "Term_wipe()" by just drawing spaces.
1554 * Note that the "Term->always_pict" flag will disable the use of the
1555 * "Term_wipe()" function entirely, and force everything, even text
1556 * drawn in the attr "black", to be explicitly drawn.
1558 * Note that if no "black" text is ever drawn, and if "attr_blank" is
1559 * not "zero", then the "Term_wipe" hook will never be used, even if
1560 * the "Term->always_text" flag is not set.
1562 * This function does nothing unless the "Term" is "mapped", which allows
1563 * certain systems to optimize the handling of "closed" windows.
1565 * On systems with a "soft" cursor, we must explicitly erase the cursor
1566 * before flushing the output, if needed, to prevent a "jumpy" refresh.
1567 * The actual method for this is horrible, but there is very little that
1568 * we can do to simplify it efficiently. XXX XXX XXX
1570 * On systems with a "hard" cursor, we will "hide" the cursor before
1571 * flushing the output, if needed, to avoid a "flickery" refresh. It
1572 * would be nice to *always* hide the cursor during the refresh, but
1573 * this might be expensive (and/or ugly) on some machines.
1575 * The "Term->icky_corner" flag is used to avoid calling "Term_wipe()"
1576 * or "Term_pict()" or "Term_text()" on the bottom right corner of the
1577 * window, which might induce "scrolling" or other nasty stuff on old
1578 * dumb terminals. This flag is handled very efficiently. We assume
1579 * that the "Term_curs()" call will prevent placing the cursor in the
1580 * corner, if needed, though I doubt such placement is ever a problem.
1581 * Currently, the use of "Term->icky_corner" and "Term->soft_cursor"
1582 * together may result in undefined behavior.
1584 errr Term_fresh(void)
1594 term_win *old = Term->old;
1595 term_win *scr = Term->scr;
1598 /* Do nothing unless "mapped" */
1599 if (!Term->mapped_flag) return (1);
1602 /* Trivial Refresh */
1604 (scr->cu == old->cu) &&
1605 (scr->cv == old->cv) &&
1606 (scr->cx == old->cx) &&
1607 (scr->cy == old->cy) &&
1608 !(Term->total_erase))
1615 /* Paranoia -- use "fake" hooks to prevent core dumps */
1616 if (!Term->curs_hook) Term->curs_hook = Term_curs_hack;
1617 if (!Term->wipe_hook) Term->wipe_hook = Term_wipe_hack;
1618 if (!Term->text_hook) Term->text_hook = Term_text_hack;
1619 if (!Term->pict_hook) Term->pict_hook = Term_pict_hack;
1622 /* Handle "total erase" */
1623 if (Term->total_erase)
1625 byte na = Term->attr_blank;
1626 char nc = Term->char_blank;
1628 /* Physically erase the entire window */
1629 Term_xtra(TERM_XTRA_CLEAR, 0);
1631 /* Hack -- clear all "cursor" data */
1632 old->cv = old->cu = old->cx = old->cy = 0;
1635 for (y = 0; y < h; y++)
1637 byte *aa = old->a[y];
1638 char *cc = old->c[y];
1640 #ifdef USE_TRANSPARENCY
1642 byte *taa = old->ta[y];
1643 char *tcc = old->tc[y];
1645 #endif /* USE_TRANSPARENCY */
1648 /* Wipe each column */
1649 for (x = 0; x < w; x++)
1651 /* Wipe each grid */
1655 #ifdef USE_TRANSPARENCY
1660 #endif /* USE_TRANSPARENCY */
1665 /* Redraw every row */
1667 Term->y2 = y2 = h - 1;
1669 /* Redraw every column */
1670 for (y = 0; y < h; y++)
1673 Term->x2[y] = w - 1;
1676 /* Forget "total erase" */
1677 Term->total_erase = FALSE;
1681 /* Cursor update -- Erase old Cursor */
1682 if (Term->soft_cursor)
1684 /* Cursor was visible */
1685 if (!old->cu && old->cv)
1690 byte *old_aa = old->a[ty];
1691 char *old_cc = old->c[ty];
1693 byte oa = old_aa[tx];
1694 char oc = old_cc[tx];
1696 #ifdef USE_TRANSPARENCY
1698 byte *old_taa = old->ta[ty];
1699 char *old_tcc = old->tc[ty];
1701 byte ota = old_taa[tx];
1702 char otc = old_tcc[tx];
1704 #endif /* USE_TRANSPARENCY */
1706 /* Hack -- use "Term_pict()" always */
1707 if (Term->always_pict)
1709 #ifdef USE_TRANSPARENCY
1710 (void)((*Term->pict_hook)(tx, ty, 1, &oa, &oc, &ota, &otc));
1711 #else /* USE_TRANSPARENCY */
1712 (void)((*Term->pict_hook)(tx, ty, 1, &oa, &oc));
1713 #endif /* USE_TRANSPARENCY */
1716 /* Hack -- use "Term_pict()" sometimes */
1717 else if (Term->higher_pict && (oa & 0x80) && (oc & 0x80))
1719 #ifdef USE_TRANSPARENCY
1720 (void)((*Term->pict_hook)(tx, ty, 1, &oa, &oc, &ota, &otc));
1721 #else /* USE_TRANSPARENCY */
1722 (void)((*Term->pict_hook)(tx, ty, 1, &oa, &oc));
1723 #endif /* USE_TRANSPARENCY */
1726 /* Hack -- restore the actual character */
1727 else if (oa || Term->always_text)
1731 send_text_to_chuukei_server(tx, ty, 1, oa, &oc);
1734 if (tx + 1 < Term->wid && !(old_aa[tx] & 0x80) && (iskanji(old_cc[tx])))
1735 /* Redraw a Kanji */
1736 (void)((*Term->text_hook)(tx, ty, 2, oa, &old_cc[tx]));
1739 (void)((*Term->text_hook)(tx, ty, 1, oa, &oc));
1742 /* Hack -- erase the grid */
1746 send_wipe_to_chuukei_server(tx, ty, 1);
1748 (void)((*Term->wipe_hook)(tx, ty, 1));
1753 /* Cursor Update -- Erase old Cursor */
1756 /* Cursor will be invisible */
1757 if (scr->cu || !scr->cv)
1759 /* Make the cursor invisible */
1760 Term_xtra(TERM_XTRA_SHAPE, 0);
1765 /* Something to update */
1768 /* Handle "icky corner" */
1769 if (Term->icky_corner)
1771 /* Avoid the corner */
1774 /* Avoid the corner */
1775 if (Term->x2[h - 1] > w - 2)
1777 /* Avoid the corner */
1778 Term->x2[h - 1] = w - 2;
1784 /* Scan the "modified" rows */
1785 for (y = y1; y <= y2; ++y)
1787 int x1 = Term->x1[y];
1788 int x2 = Term->x2[y];
1790 /* Flush each "modified" row */
1793 /* Always use "Term_pict()" */
1794 if (Term->always_pict)
1797 Term_fresh_row_pict(y, x1, x2);
1800 /* Sometimes use "Term_pict()" */
1801 else if (Term->higher_pict)
1804 Term_fresh_row_both(y, x1, x2);
1807 /* Never use "Term_pict()" */
1811 Term_fresh_row_text(y, x1, x2);
1814 /* This row is all done */
1818 /* Hack -- Flush that row (if allowed) */
1819 if (!Term->never_frosh) Term_xtra(TERM_XTRA_FROSH, y);
1823 /* No rows are invalid */
1829 /* Cursor update -- Show new Cursor */
1830 if (Term->soft_cursor)
1832 /* Draw the cursor */
1833 if (!scr->cu && scr->cv)
1836 send_curs_to_chuukei_server(scr->cx, scr->cy);
1838 /* Call the cursor display routine */
1839 (void)((*Term->curs_hook)(scr->cx, scr->cy));
1843 /* Cursor Update -- Show new Cursor */
1846 /* The cursor is useless, hide it */
1850 send_curs_to_chuukei_server(w - 1, scr->cy);
1852 /* Paranoia -- Put the cursor NEAR where it belongs */
1853 (void)((*Term->curs_hook)(w - 1, scr->cy));
1855 /* Make the cursor invisible */
1856 /* Term_xtra(TERM_XTRA_SHAPE, 0); */
1859 /* The cursor is invisible, hide it */
1863 send_curs_to_chuukei_server(scr->cx, scr->cy);
1865 /* Paranoia -- Put the cursor where it belongs */
1866 (void)((*Term->curs_hook)(scr->cx, scr->cy));
1868 /* Make the cursor invisible */
1869 /* Term_xtra(TERM_XTRA_SHAPE, 0); */
1872 /* The cursor is visible, display it correctly */
1876 send_curs_to_chuukei_server(scr->cx, scr->cy);
1878 /* Put the cursor where it belongs */
1879 (void)((*Term->curs_hook)(scr->cx, scr->cy));
1881 /* Make the cursor visible */
1882 Term_xtra(TERM_XTRA_SHAPE, 1);
1887 /* Save the "cursor state" */
1894 /* Actually flush the output */
1895 Term_xtra(TERM_XTRA_FRESH, 0);
1904 /*** Output routines ***/
1908 * Set the cursor visibility
1910 errr Term_set_cursor(int v)
1913 if (Term->scr->cv == v) return (1);
1924 * Place the cursor at a given location
1926 * Note -- "illegal" requests do not move the cursor.
1928 errr Term_gotoxy(int x, int y)
1934 if ((x < 0) || (x >= w)) return (-1);
1935 if ((y < 0) || (y >= h)) return (-1);
1937 /* Remember the cursor */
1941 /* The cursor is not useless */
1950 * At a given location, place an attr/char
1951 * Do not change the cursor position
1952 * No visual changes until "Term_fresh()".
1954 errr Term_draw(int x, int y, byte a, char c)
1959 /* Verify location */
1960 if ((x < 0) || (x >= w)) return (-1);
1961 if ((y < 0) || (y >= h)) return (-1);
1963 /* Paranoia -- illegal char */
1964 if (!c) return (-2);
1966 /* Queue it for later */
1967 #ifdef USE_TRANSPARENCY
1968 Term_queue_char(x, y, a, c, 0, 0);
1969 #else /* USE_TRANSPARENCY */
1970 Term_queue_char(x, y, a, c);
1971 #endif /* USE_TRANSPARENCY */
1979 * Using the given attr, add the given char at the cursor.
1981 * We return "-2" if the character is "illegal". XXX XXX
1983 * We return "-1" if the cursor is currently unusable.
1985 * We queue the given attr/char for display at the current
1986 * cursor location, and advance the cursor to the right,
1987 * marking it as unuable and returning "1" if it leaves
1988 * the screen, and otherwise returning "0".
1990 * So when this function, or the following one, return a
1991 * positive value, future calls to either function will
1992 * return negative ones.
1994 errr Term_addch(byte a, char c)
1998 /* Handle "unusable" cursor */
1999 if (Term->scr->cu) return (-1);
2001 /* Paranoia -- no illegal chars */
2002 if (!c) return (-2);
2004 /* Queue the given character for display */
2005 #ifdef USE_TRANSPARENCY
2006 Term_queue_char(Term->scr->cx, Term->scr->cy, a, c, 0, 0);
2007 #else /* USE_TRANSPARENCY */
2008 Term_queue_char(Term->scr->cx, Term->scr->cy, a, c);
2009 #endif /* USE_TRANSPARENCY */
2011 /* Advance the cursor */
2015 if (Term->scr->cx < w) return (0);
2017 /* Note "Useless" cursor */
2020 /* Note "Useless" cursor */
2026 * At the current location, using an attr, add a string
2028 * We also take a length "n", using negative values to imply
2029 * the largest possible value, and then we use the minimum of
2030 * this length and the "actual" length of the string as the
2031 * actual number of characters to attempt to display, never
2032 * displaying more characters than will actually fit, since
2033 * we do NOT attempt to "wrap" the cursor at the screen edge.
2035 * We return "-1" if the cursor is currently unusable.
2036 * We return "N" if we were "only" able to write "N" chars,
2037 * even if all of the given characters fit on the screen,
2038 * and mark the cursor as unusable for future attempts.
2040 * So when this function, or the preceding one, return a
2041 * positive value, future calls to either function will
2042 * return negative ones.
2044 errr Term_addstr(int n, byte a, cptr s)
2052 /* Handle "unusable" cursor */
2053 if (Term->scr->cu) return (-1);
2055 /* Obtain maximal length */
2056 k = (n < 0) ? (w + 1) : n;
2058 /* Obtain the usable string length */
2059 for (n = 0; (n < k) && s[n]; n++) /* loop */;
2061 /* React to reaching the edge of the screen */
2062 if (Term->scr->cx + n >= w) res = n = w - Term->scr->cx;
2064 /* Queue the first "n" characters for display */
2065 Term_queue_chars(Term->scr->cx, Term->scr->cy, n, a, s);
2067 /* Advance the cursor */
2070 /* Hack -- Notice "Useless" cursor */
2071 if (res) Term->scr->cu = 1;
2073 /* Success (usually) */
2079 * Move to a location and, using an attr, add a char
2081 errr Term_putch(int x, int y, byte a, char c)
2086 if ((res = Term_gotoxy(x, y)) != 0) return (res);
2088 /* Then add the char */
2089 if ((res = Term_addch(a, c)) != 0) return (res);
2097 * Move to a location and, using an attr, add a string
2099 errr Term_putstr(int x, int y, int n, byte a, cptr s)
2104 if ((res = Term_gotoxy(x, y)) != 0) return (res);
2106 /* Then add the string */
2107 if ((res = Term_addstr(n, a, s)) != 0) return (res);
2115 * Move to a location and, using an attr, add a string vertically
2117 errr Term_putstr_v(int x, int y, int n, byte a, cptr s)
2124 for (i = 0; i < n && s[i] != 0; i++)
2127 if ((res = Term_gotoxy(x, y0)) != 0) return (res);
2131 if ((res = Term_addstr(2, a, &s[i])) != 0) return (res);
2134 if (s[i] == 0) break;
2136 if ((res = Term_addstr(1, a, &s[i])) != 0) return (res);
2147 * Place cursor at (x,y), and clear the next "n" chars
2149 errr Term_erase(int x, int y, int n)
2154 /* int h = Term->hgt; */
2159 int na = Term->attr_blank;
2160 int nc = Term->char_blank;
2165 #ifdef USE_TRANSPARENCY
2168 #endif /* USE_TRANSPARENCY */
2171 if (Term_gotoxy(x, y)) return (-1);
2173 /* Force legal size */
2174 if (x + n > w) n = w - x;
2177 scr_aa = Term->scr->a[y];
2178 scr_cc = Term->scr->c[y];
2180 #ifdef USE_TRANSPARENCY
2181 scr_taa = Term->scr->ta[y];
2182 scr_tcc = Term->scr->tc[y];
2183 #endif /* USE_TRANSPARENCY */
2187 * Á´³Ñʸ»ú¤Î±¦È¾Ê¬¤«¤éʸ»ú¤òɽ¼¨¤¹¤ë¾ì¹ç¡¢
2188 * ½Å¤Ê¤Ã¤¿Ê¸»ú¤Îº¸Éôʬ¤ò¾Ãµî¡£
2190 if (n > 0 && (scr_aa[x] & KANJI2))
2192 if (n > 0 && (byte)scr_cc[x] == 255 && scr_aa[x] == 255)
2199 /* Scan every column */
2200 for (i = 0; i < n; i++, x++)
2205 /* Hack -- Ignore "non-changes" */
2206 if ((oa == na) && (oc == nc)) continue;
2210 * Á´³Ñʸ»ú¤Îº¸È¾Ê¬¤Çɽ¼¨¤ò½ªÎ»¤¹¤ë¾ì¹ç¡¢
2211 * ½Å¤Ê¤Ã¤¿Ê¸»ú¤Î±¦Éôʬ¤ò¾Ãµî¡£
2213 * 2001/04/29 -- Habu
2214 * ¹Ô¤Î±¦Ã¼¤Î¾ì¹ç¤Ï¤³¤Î½èÍý¤ò¤·¤Ê¤¤¤è¤¦¤Ë½¤Àµ¡£
2216 if ((oa & KANJI1) && (i + 1) == n && x != w - 1)
2219 /* Save the "literal" information */
2223 #ifdef USE_TRANSPARENCY
2226 #endif /* USE_TRANSPARENCY */
2228 /* Track minimum changed column */
2231 /* Track maximum changed column */
2235 /* Expand the "change area" as needed */
2238 /* Check for new min/max row info */
2239 if (y < Term->y1) Term->y1 = y;
2240 if (y > Term->y2) Term->y2 = y;
2242 /* Check for new min/max col info in this row */
2243 if (x1 < Term->x1[y]) Term->x1[y] = x1;
2244 if (x2 > Term->x2[y]) Term->x2[y] = x2;
2253 * Clear the entire window, and move to the top left corner
2255 * Note the use of the special "total_erase" code
2257 errr Term_clear(void)
2264 byte na = Term->attr_blank;
2265 char nc = Term->char_blank;
2270 /* Cursor to the top left */
2271 Term->scr->cx = Term->scr->cy = 0;
2274 for (y = 0; y < h; y++)
2276 byte *scr_aa = Term->scr->a[y];
2277 char *scr_cc = Term->scr->c[y];
2279 #ifdef USE_TRANSPARENCY
2280 byte *scr_taa = Term->scr->ta[y];
2281 char *scr_tcc = Term->scr->tc[y];
2282 #endif /* USE_TRANSPARENCY */
2284 /* Wipe each column */
2285 for (x = 0; x < w; x++)
2290 #ifdef USE_TRANSPARENCY
2293 #endif /* USE_TRANSPARENCY */
2297 /* This row has changed */
2299 Term->x2[y] = w - 1;
2302 /* Every row has changed */
2306 /* Force "total erase" */
2307 Term->total_erase = TRUE;
2318 * Redraw (and refresh) the whole window.
2320 errr Term_redraw(void)
2322 /* Force "total erase" */
2323 Term->total_erase = TRUE;
2325 /* Hack -- Refresh */
2334 * Redraw part of a widow.
2336 errr Term_redraw_section(int x1, int y1, int x2, int y2)
2342 /* Bounds checking */
2343 if (y2 >= Term->hgt) y2 = Term->hgt - 1;
2344 if (x2 >= Term->wid) x2 = Term->wid - 1;
2352 /* Set the x limits */
2353 for (i = Term->y1; i <= Term->y2; i++)
2361 if (Term->scr->a[i][x1j] & KANJI2) x1j--;
2364 if (x2j < Term->wid - 1)
2366 if (Term->scr->a[i][x2j] & KANJI1) x2j++;
2372 c_ptr = Term->old->c[i];
2374 /* Clear the section so it is redrawn */
2375 for (j = x1j; j <= x2j; j++)
2377 /* Hack - set the old character to "none" */
2384 c_ptr = Term->old->c[i];
2386 /* Clear the section so it is redrawn */
2387 for (j = x1; j <= x2; j++)
2389 /* Hack - set the old character to "none" */
2395 /* Hack -- Refresh */
2404 /*** Access routines ***/
2408 * Extract the cursor visibility
2410 errr Term_get_cursor(int *v)
2412 /* Extract visibility */
2413 (*v) = Term->scr->cv;
2421 * Extract the current window size
2423 errr Term_get_size(int *w, int *h)
2425 /* Access the cursor */
2435 * Extract the current cursor location
2437 errr Term_locate(int *x, int *y)
2439 /* Access the cursor */
2440 (*x) = Term->scr->cx;
2441 (*y) = Term->scr->cy;
2443 /* Warn about "useless" cursor */
2444 if (Term->scr->cu) return (1);
2452 * At a given location, determine the "current" attr and char
2453 * Note that this refers to what will be on the window after the
2454 * next call to "Term_fresh()". It may or may not already be there.
2456 errr Term_what(int x, int y, byte *a, char *c)
2461 /* Verify location */
2462 if ((x < 0) || (x >= w)) return (-1);
2463 if ((y < 0) || (y >= h)) return (-1);
2466 (*a) = Term->scr->a[y][x];
2467 (*c) = Term->scr->c[y][x];
2475 /*** Input routines ***/
2479 * Flush and forget the input
2481 errr Term_flush(void)
2483 /* Hack -- Flush all events */
2484 Term_xtra(TERM_XTRA_FLUSH, 0);
2486 /* Forget all keypresses */
2487 Term->key_head = Term->key_tail = 0;
2496 * Add a keypress to the "queue"
2498 errr Term_keypress(int k)
2500 /* Hack -- Refuse to enqueue non-keys */
2501 if (!k) return (-1);
2503 /* Store the char, advance the queue */
2504 Term->key_queue[Term->key_head++] = k;
2506 /* Circular queue, handle wrap */
2507 if (Term->key_head == Term->key_size) Term->key_head = 0;
2509 /* Success (unless overflow) */
2510 if (Term->key_head != Term->key_tail) return (0);
2513 /* Hack -- Forget the oldest key */
2514 if (++Term->key_tail == Term->key_size) Term->key_tail = 0;
2523 * Add a keypress to the FRONT of the "queue"
2525 errr Term_key_push(int k)
2527 /* Hack -- Refuse to enqueue non-keys */
2528 if (!k) return (-1);
2530 /* Hack -- Overflow may induce circular queue */
2531 if (Term->key_tail == 0) Term->key_tail = Term->key_size;
2533 /* Back up, Store the char */
2534 Term->key_queue[--Term->key_tail] = k;
2536 /* Success (unless overflow) */
2537 if (Term->key_head != Term->key_tail) return (0);
2540 /* Hack -- Forget the oldest key */
2541 if (++Term->key_tail == Term->key_size) Term->key_tail = 0;
2553 * Check for a pending keypress on the key queue.
2555 * Store the keypress, if any, in "ch", and return "0".
2556 * Otherwise store "zero" in "ch", and return "1".
2558 * Wait for a keypress if "wait" is true.
2560 * Remove the keypress if "take" is true.
2562 errr Term_inkey(char *ch, bool wait, bool take)
2571 /* Hack -- get bored */
2572 if (!Term->never_bored)
2574 /* Process random events */
2575 Term_xtra(TERM_XTRA_BORED, 0);
2581 /* Process pending events while necessary */
2582 while (Term->key_head == Term->key_tail)
2584 /* Process events (wait for one) */
2585 Term_xtra(TERM_XTRA_EVENT, TRUE);
2592 /* Process pending events if necessary */
2593 if (Term->key_head == Term->key_tail)
2595 /* Process events (do not wait) */
2596 Term_xtra(TERM_XTRA_EVENT, FALSE);
2600 /* No keys are ready */
2601 if (Term->key_head == Term->key_tail) return (1);
2603 /* Extract the next keypress */
2604 (*ch) = Term->key_queue[Term->key_tail];
2606 /* If requested, advance the queue, wrap around if necessary */
2607 if (take && (++Term->key_tail == Term->key_size)) Term->key_tail = 0;
2615 /*** Extra routines ***/
2619 * Save the "requested" screen into the "memorized" screen
2621 * Every "Term_save()" should match exactly one "Term_load()"
2623 errr Term_save(void)
2631 /* Allocate window */
2632 MAKE(Term->mem, term_win);
2634 /* Initialize window */
2635 term_win_init(Term->mem, w, h);
2639 term_win_copy(Term->mem, Term->scr, w, h);
2647 * Restore the "requested" contents (see above).
2649 * Every "Term_save()" should match exactly one "Term_load()"
2651 errr Term_load(void)
2661 /* Allocate window */
2662 MAKE(Term->mem, term_win);
2664 /* Initialize window */
2665 term_win_init(Term->mem, w, h);
2669 term_win_copy(Term->scr, Term->mem, w, h);
2672 for (y = 0; y < h; y++)
2676 Term->x2[y] = w - 1;
2689 * Exchange the "requested" screen with the "tmp" screen
2691 errr Term_exchange(void)
2698 term_win *exchanger;
2704 /* Allocate window */
2705 MAKE(Term->tmp, term_win);
2707 /* Initialize window */
2708 term_win_init(Term->tmp, w, h);
2712 exchanger = Term->scr;
2713 Term->scr = Term->tmp;
2714 Term->tmp = exchanger;
2717 for (y = 0; y < h; y++)
2721 Term->x2[y] = w - 1;
2735 * React to a new physical window size.
2737 errr Term_resize(int w, int h)
2751 /* Resizing is forbidden */
2752 if (Term->fixed_shape) return (-1);
2754 /* Ignore illegal changes */
2755 if ((w < 1) || (h < 1)) return (-1);
2758 /* Ignore non-changes */
2759 if ((Term->wid == w) && (Term->hgt == h) && (arg_bigtile == use_bigtile))
2762 use_bigtile = arg_bigtile;
2764 /* Minimum dimensions */
2765 wid = MIN(Term->wid, w);
2766 hgt = MIN(Term->hgt, h);
2772 /* Save old window */
2773 hold_old = Term->old;
2775 /* Save old window */
2776 hold_scr = Term->scr;
2778 /* Save old window */
2779 hold_mem = Term->mem;
2781 /* Save old window */
2782 hold_tmp = Term->tmp;
2784 /* Create new scanners */
2785 C_MAKE(Term->x1, h, byte);
2786 C_MAKE(Term->x2, h, byte);
2788 /* Create new window */
2789 MAKE(Term->old, term_win);
2791 /* Initialize new window */
2792 term_win_init(Term->old, w, h);
2794 /* Save the contents */
2795 term_win_copy(Term->old, hold_old, wid, hgt);
2797 /* Create new window */
2798 MAKE(Term->scr, term_win);
2800 /* Initialize new window */
2801 term_win_init(Term->scr, w, h);
2803 /* Save the contents */
2804 term_win_copy(Term->scr, hold_scr, wid, hgt);
2809 /* Create new window */
2810 MAKE(Term->mem, term_win);
2812 /* Initialize new window */
2813 term_win_init(Term->mem, w, h);
2815 /* Save the contents */
2816 term_win_copy(Term->mem, hold_mem, wid, hgt);
2822 /* Create new window */
2823 MAKE(Term->tmp, term_win);
2825 /* Initialize new window */
2826 term_win_init(Term->tmp, w, h);
2828 /* Save the contents */
2829 term_win_copy(Term->tmp, hold_tmp, wid, hgt);
2832 /* Free some arrays */
2833 C_KILL(hold_x1, Term->hgt, byte);
2834 C_KILL(hold_x2, Term->hgt, byte);
2837 term_win_nuke(hold_old, Term->wid, Term->hgt);
2840 KILL(hold_old, term_win);
2842 /* Illegal cursor */
2843 if (Term->old->cx >= w) Term->old->cu = 1;
2844 if (Term->old->cy >= h) Term->old->cu = 1;
2847 term_win_nuke(hold_scr, Term->wid, Term->hgt);
2850 KILL(hold_scr, term_win);
2852 /* Illegal cursor */
2853 if (Term->scr->cx >= w) Term->scr->cu = 1;
2854 if (Term->scr->cy >= h) Term->scr->cu = 1;
2860 term_win_nuke(hold_mem, Term->wid, Term->hgt);
2863 KILL(hold_mem, term_win);
2865 /* Illegal cursor */
2866 if (Term->mem->cx >= w) Term->mem->cu = 1;
2867 if (Term->mem->cy >= h) Term->mem->cu = 1;
2874 term_win_nuke(hold_tmp, Term->wid, Term->hgt);
2877 KILL(hold_tmp, term_win);
2879 /* Illegal cursor */
2880 if (Term->tmp->cx >= w) Term->tmp->cu = 1;
2881 if (Term->tmp->cy >= h) Term->tmp->cu = 1;
2888 /* Force "total erase" */
2889 Term->total_erase = TRUE;
2892 for (i = 0; i < h; i++)
2896 Term->x2[i] = w - 1;
2903 /* Execute the "resize_hook" hook, if available */
2904 if (Term->resize_hook)
2906 Term->resize_hook();
2916 * Activate a new Term (and deactivate the current Term)
2918 * This function is extremely important, and also somewhat bizarre.
2919 * It is the only function that should "modify" the value of "Term".
2921 * To "create" a valid "term", one should do "term_init(t)", then
2922 * set the various flags and hooks, and then do "Term_activate(t)".
2924 errr Term_activate(term *t)
2926 /* Hack -- already done */
2927 if (Term == t) return (1);
2929 /* Deactivate the old Term */
2930 if (Term) Term_xtra(TERM_XTRA_LEVEL, 0);
2932 /* Hack -- Call the special "init" hook */
2933 if (t && !t->active_flag)
2935 /* Call the "init" hook */
2936 if (t->init_hook) (*t->init_hook)(t);
2939 t->active_flag = TRUE;
2942 t->mapped_flag = TRUE;
2945 /* Remember the Term */
2948 /* Activate the new Term */
2949 if (Term) Term_xtra(TERM_XTRA_LEVEL, 1);
2960 errr term_nuke(term *t)
2966 /* Hack -- Call the special "nuke" hook */
2969 /* Call the "nuke" hook */
2970 if (t->nuke_hook) (*t->nuke_hook)(t);
2973 t->active_flag = FALSE;
2975 /* Assume not mapped */
2976 t->mapped_flag = FALSE;
2980 /* Nuke "displayed" */
2981 term_win_nuke(t->old, w, h);
2983 /* Kill "displayed" */
2984 KILL(t->old, term_win);
2986 /* Nuke "requested" */
2987 term_win_nuke(t->scr, w, h);
2989 /* Kill "requested" */
2990 KILL(t->scr, term_win);
2995 /* Nuke "memorized" */
2996 term_win_nuke(t->mem, w, h);
2998 /* Kill "memorized" */
2999 KILL(t->mem, term_win);
3005 /* Nuke "temporary" */
3006 term_win_nuke(t->tmp, w, h);
3008 /* Kill "temporary" */
3009 KILL(t->tmp, term_win);
3012 /* Free some arrays */
3013 C_KILL(t->x1, h, byte);
3014 C_KILL(t->x2, h, byte);
3016 /* Free the input queue */
3017 C_KILL(t->key_queue, t->key_size, char);
3025 * Initialize a term, using a window of the given size.
3026 * Also prepare the "input queue" for "k" keypresses
3027 * By default, the cursor starts out "invisible"
3028 * By default, we "erase" using "black spaces"
3030 errr term_init(term *t, int w, int h, int k)
3036 (void)WIPE(t, term);
3039 /* Prepare the input queue */
3040 t->key_head = t->key_tail = 0;
3042 /* Determine the input queue size */
3045 /* Allocate the input queue */
3046 C_MAKE(t->key_queue, t->key_size, char);
3053 /* Allocate change arrays */
3054 C_MAKE(t->x1, h, byte);
3055 C_MAKE(t->x2, h, byte);
3058 /* Allocate "displayed" */
3059 MAKE(t->old, term_win);
3061 /* Initialize "displayed" */
3062 term_win_init(t->old, w, h);
3065 /* Allocate "requested" */
3066 MAKE(t->scr, term_win);
3068 /* Initialize "requested" */
3069 term_win_init(t->scr, w, h);
3073 for (y = 0; y < h; y++)
3084 /* Force "total erase" */
3085 t->total_erase = TRUE;
3088 /* Default "blank" */
3090 t->char_blank = ' ';