OSDN Git Service

v3.0.0 Alpha5 OSDN最終版
[hengband/hengband.git] / src / z-term.c
1 /* File: z-term.c */
2
3 /*
4  * Copyright (c) 1997 Ben Harrison
5  *
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.
9  */
10
11 /* Purpose: a generic, efficient, terminal window package -BEN- */
12 #include "angband.h"
13
14 #include "z-term.h"
15
16 #include "z-virt.h"
17
18 /* Special flags in the attr data */
19 #define AF_BIGTILE2 0xf0
20 #define AF_TILE1   0x80
21
22 #ifdef JP
23 #define AF_KANJI1  0x10
24 #define AF_KANJI2  0x20
25 #define AF_KANJIC  0x0f
26 /*
27  * 全角文字対応。
28  * 属性に全角文字の1バイト目、2バイト目も記憶。
29  * By FIRST
30  */
31 #endif
32 /*
33  * This file provides a generic, efficient, terminal window package,
34  * which can be used not only on standard terminal environments such
35  * as dumb terminals connected to a Unix box, but also in more modern
36  * "graphic" environments, such as the Macintosh or Unix/X11.
37  *
38  * Each "window" works like a standard "dumb terminal", that is, it
39  * can display a two dimensional array of grids containing colored
40  * textual symbols, plus an optional cursor, and it can be used to
41  * get keypress events from the user.
42  *
43  * In fact, this package can simply be used, if desired, to support
44  * programs which will look the same on a dumb terminal as they do
45  * on a graphic platform such as the Macintosh.
46  *
47  * This package was designed to help port the game "Angband" to a wide
48  * variety of different platforms.  Angband, like many other games in
49  * the "rogue-like" heirarchy, requires, at the minimum, the ability
50  * to display "colored textual symbols" in a standard 80x24 "window",
51  * such as that provided by most dumb terminals, and many old personal
52  * computers, and to check for "keypresses" from the user.  The major
53  * concerns were thus portability and efficiency, so Angband could be
54  * easily ported to many different systems, with minimal effort, and
55  * yet would run quickly on each of these sys
56  tems, no matter what kind
57  * of underlying hardware/software support was being used.
58  *
59  * It is important to understand the differences between the older
60  * "dumb terminals" and the newer "graphic interface" machines, since
61  * this package was designed to work with both types of systems.
62  *
63  * New machines:
64  *   waiting for a keypress is complex
65  *   checking for a keypress is often cheap
66  *   changing "colors" may be expensive
67  *   the "color" of a "blank" is rarely important
68  *   moving the "cursor" is relatively cheap
69  *   use a "software" cursor (only moves when requested)
70  *   drawing characters normally will not erase old ones
71  *   drawing a character on the cursor often erases it
72  *   may have fast routines for "clear a region"
73  *   the bottom right corner is usually not special
74  *
75  * Old machines:
76  *   waiting for a keypress is simple
77  *   checking for a keypress is often expensive
78  *   changing "colors" is usually cheap
79  *   the "color" of a "blank" may be important
80  *   moving the "cursor" may be expensive
81  *   use a "hardware" cursor (moves during screen updates)
82  *   drawing new symbols automatically erases old ones
83  *   characters may only be drawn at the cursor location
84  *   drawing a character on the cursor will move the cursor
85  *   may have fast routines for "clear entire window"
86  *   may have fast routines for "clear to end of line"
87  *   the bottom right corner is often dangerous
88  *
89  *
90  * This package provides support for multiple windows, each of an
91  * arbitrary size (up to 255x255), each with its own set of flags,
92  * and its own hooks to handle several low-level procedures which
93  * differ from platform to platform.  Then the main program simply
94  * creates one or more "term" structures, setting the various flags
95  * and hooks in a manner appropriate for the current platform, and
96  * then it can use the various "term" structures without worrying
97  * about the underlying platform.
98  *
99  *
100  * This package allows each "grid" in each window to hold an attr/char
101  * pair, with each ranging from 0 to 255, and makes very few assumptions
102  * about the meaning of any attr/char values.  Normally, we assume that
103  * "attr 0" is "black", with the semantics that "black" text should be
104  * sent to "Term_wipe()" instead of "Term_text()", but this sematics is
105  * modified if either the "always_pict" or the "always_text" flags are
106  * set.  We assume that "char 0" is "dangerous", since placing such a
107  * "char" in the middle of a string "terminates" the string, and usually
108  * we prevent its use.
109  *
110  * Finally, we use a special attr/char pair, defaulting to "attr 0" and
111  * "char 32", also known as "black space", when we "erase" or "clear"
112  * any window, but this pair can be redefined to any pair, including
113  * the standard "white space", or the bizarre "emptiness" ("attr 0"
114  * and "char 0"), as long as various obscure restrictions are met.
115  *
116  *
117  * This package provides several functions which allow a program to
118  * interact with the "term" structures.  Most of the functions allow
119  * the program to "request" certain changes to the current "term",
120  * such as moving the cursor, drawing an attr/char pair, erasing a
121  * region of grids, hiding the cursor, etc.  Then there is a special
122  * function which causes all of the "pending" requests to be performed
123  * in an efficient manner.  There is another set of functions which
124  * allow the program to query the "requested state" of the current
125  * "term", such as asking for the cursor location, or what attr/char
126  * is at a given location, etc.  There is another set of functions
127  * dealing with "keypress" events, which allows the program to ask if
128  * the user has pressed any keys, or to forget any keys the user pressed.
129  * There is a pair of functions to allow this package to memorize the
130  * contents of the current "term", and to restore these contents at
131  * a later time.  There is a special function which allows the program
132  * to specify which "term" structure should be the "current" one.  At
133  * the lowest level, there is a set of functions which allow a new
134  * "term" to be initialized or destroyed, and which allow this package,
135  * or a program, to access the special "hooks" defined for the current
136  * "term", and a set of functions which those "hooks" can use to inform
137  * this package of the results of certain occurances, for example, one
138  * such function allows this package to learn about user keypresses,
139  * detected by one of the special "hooks".
140  *
141  * We provide, among other things, the functions "Term_keypress()"
142  * to "react" to keypress events, and "Term_redraw()" to redraw the
143  * entire window, plus "Term_resize()" to note a new size.
144  *
145  *
146  * Note that the current "term" contains two "window images".  One of
147  * these images represents the "requested" contents of the "term", and
148  * the other represents the "actual" contents of the "term", at the time
149  * of the last performance of pending requests.  This package uses these
150  * two images to determine the "minimal" amount of work needed to make
151  * the "actual" contents of the "term" match the "requested" contents of
152  * the "term".  This method is not perfect, but it often reduces the
153  * amount of work needed to perform the pending requests, which thus
154  * increases the speed of the program itself.  This package promises
155  * that the requested changes will appear to occur either "all at once"
156  * or in a "top to bottom" order.  In addition, a "cursor" is maintained,
157  * and this cursor is updated along with the actual window contents.
158  *
159  * Currently, the "Term_fresh()" routine attempts to perform the "minimum"
160  * number of physical updates, in terms of total "work" done by the hooks
161  * Term_wipe(), Term_text(), and Term_pict(), making use of the fact that
162  * adjacent characters of the same color can both be drawn together using
163  * the "Term_text()" hook, and that "black" text can often be sent to the
164  * "Term_wipe()" hook instead of the "Term_text()" hook, and if something
165  * is already displayed in a window, then it is not necessary to display
166  * it again.  Unfortunately, this may induce slightly non-optimal results
167  * in some cases, in particular, those in which, say, a string of ten
168  * characters needs to be written, but the fifth character has already
169  * been displayed.  Currently, this will cause the "Term_text()" routine
170  * to be called once for each half of the string, instead of once for the
171  * whole string, which, on some machines, may be non-optimal behavior.
172  *
173  * The new formalism includes a "displayed" screen image (old) which
174  * is actually seen by the user, a "requested" screen image (scr)
175  * which is being prepared for display, a "memorized" screen image
176  * (mem) which is used to save and restore screen images, and a
177  * "temporary" screen image (tmp) which is currently unused.
178  *
179  *
180  * Several "flags" are available in each "term" to allow the underlying
181  * visual system (which initializes the "term" structure) to "optimize"
182  * the performance of this package for the given system, or to request
183  * certain behavior which is helpful/required for the given system.
184  *
185  * The "soft_cursor" flag indicates the use of a "soft" cursor, which
186  * only moves when explicitly requested,and which is "erased" when
187  * any characters are drawn on top of it.  This flag is used for all
188  * "graphic" systems which handle the cursor by "drawing" it.
189  *
190  * The "icky_corner" flag indicates that the bottom right "corner"
191  * of the windows are "icky", and "printing" anything there may
192  * induce "messy" behavior, such as "scrolling".  This flag is used
193  * for most old "dumb terminal" systems.
194  *
195  *
196  * The "term" structure contains the following function "hooks":
197  *
198  *   Term->init_hook = Init the term
199  *   Term->nuke_hook = Nuke the term
200  *   Term->user_hook = Perform user actions
201  *   Term->xtra_hook = Perform extra actions
202  *   Term->curs_hook = Draw (or Move) the cursor
203  *   Term->bigcurs_hook = Draw (or Move) the big cursor (bigtile mode)
204  *   Term->wipe_hook = Draw some blank spaces
205  *   Term->text_hook = Draw some text in the window
206  *   Term->pict_hook = Draw some attr/chars in the window
207  *
208  * The "Term->user_hook" hook provides a simple hook to an implementation
209  * defined function, with application defined semantics.  It is available
210  * to the program via the "Term_user()" function.
211  *
212  * The "Term->xtra_hook" hook provides a variety of different functions,
213  * based on the first parameter (which should be taken from the various
214  * TERM_XTRA_* defines) and the second parameter (which may make sense
215  * only for some first parameters).  It is available to the program via
216  * the "Term_xtra()" function, though some first parameters are only
217  * "legal" when called from inside this package.
218  *
219  * The "Term->curs_hook" hook provides this package with a simple way
220  * to "move" or "draw" the cursor to the grid "x,y", depending on the
221  * setting of the "soft_cursor" flag.  Note that the cursor is never
222  * redrawn if "nothing" has happened to the screen (even temporarily).
223  * This hook is required.
224  *
225  * The "Term->wipe_hook" hook provides this package with a simple way
226  * to "erase", starting at "x,y", the next "n" grids.  This hook assumes
227  * that the input is valid.  This hook is required, unless the setting
228  * of the "always_pict" or "always_text" flags makes it optional.
229  *
230  * The "Term->text_hook" hook provides this package with a simple way
231  * to "draw", starting at "x,y", the "n" chars contained in "cp", using
232  * the attr "a".  This hook assumes that the input is valid, and that
233  * "n" is between 1 and 256 inclusive, but it should NOT assume that
234  * the contents of "cp" are null-terminated.  This hook is required,
235  * unless the setting of the "always_pict" flag makes it optional.
236  *
237  * The "Term->pict_hook" hook provides this package with a simple way
238  * to "draw", starting at "x,y", the "n" attr/char pairs contained in
239  * the arrays "ap" and "cp".  This hook assumes that the input is valid,
240  * and that "n" is between 1 and 256 inclusive, but it should NOT assume
241  * that the contents of "cp" are null-terminated.  This hook is optional,
242  * unless the setting of the "always_pict" or "higher_pict" flags make
243  * it required.  Note that recently, this hook was changed from taking
244  * a byte "a" and a char "c" to taking a length "n", an array of bytes
245  * "ap" and an array of chars "cp".  Old implementations of this hook
246  * should now iterate over all "n" attr/char pairs.
247  *
248  *
249  * The game "Angband" uses a set of files called "main-xxx.c", for
250  * various "xxx" suffixes.  Most of these contain a function called
251  * "init_xxx()", that will prepare the underlying visual system for
252  * use with Angband, and then create one or more "term" structures,
253  * using flags and hooks appropriate to the given platform, so that
254  * the "main()" function can call one (or more) of the "init_xxx()"
255  * functions, as appropriate, to prepare the required "term" structs
256  * (one for each desired sub-window), and these "init_xxx()" functions
257  * are called from a centralized "main()" function in "main.c".  Other
258  * "main-xxx.c" systems contain their own "main()" function which, in
259  * addition to doing everything needed to initialize the actual program,
260  * also does everything that the normal "init_xxx()" functions would do.
261  *
262  * The game "Angband" defines, in addition to "attr 0", all of the
263  * attr codes from 1 to 15, using definitions in "defines.h", and
264  * thus the "main-xxx.c" files used by Angband must handle these
265  * attr values correctly.  Also, they must handle all other attr
266  * values, though they may do so in any way they wish, for example,
267  * by always taking every attr code mod 16.  Many of the "main-xxx.c"
268  * files use "white space" ("attr 1" / "char 32") to "erase" or "clear"
269  * any window, for efficiency.
270  *
271  * The game "Angband" uses the "Term_user" hook to allow any of the
272  * "main-xxx.c" files to interact with the user, by calling this hook
273  * whenever the user presses the "!" key when the game is waiting for
274  * a new command.  This could be used, for example, to provide "unix
275  * shell commands" to the Unix versions of the game.
276  *
277  * See "main-xxx.c" for a simple skeleton file which can be used to
278  * create a "visual system" for a new platform when porting Angband.
279  */
280
281
282
283
284
285
286 /*
287  * The current "term"
288  */
289 term *Term = NULL;
290
291
292
293
294 /*** Local routines ***/
295
296
297 /*
298  * Nuke a term_win (see below)
299  */
300 static errr term_win_nuke(term_win *s, TERM_LEN w, TERM_LEN h)
301 {
302         /* Free the window access arrays */
303         C_KILL(s->a, h, TERM_COLOR*);
304         C_KILL(s->c, h, char*);
305
306         /* Free the window content arrays */
307         C_KILL(s->va, h * w, TERM_COLOR);
308         C_KILL(s->vc, h * w, char);
309
310         /* Free the terrain access arrays */
311         C_KILL(s->ta, h, TERM_COLOR*);
312         C_KILL(s->tc, h, char*);
313
314         /* Free the terrain content arrays */
315         C_KILL(s->vta, h * w, TERM_COLOR);
316         C_KILL(s->vtc, h * w, char);
317
318         /* Success */
319         return (0);
320 }
321
322
323 /*
324  * Initialize a "term_win" (using the given window size)
325  */
326 static errr term_win_init(term_win *s, TERM_LEN w, TERM_LEN h)
327 {
328         TERM_LEN y;
329
330         /* Make the window access arrays */
331         C_MAKE(s->a, h, TERM_COLOR*);
332         C_MAKE(s->c, h, char*);
333
334         /* Make the window content arrays */
335         C_MAKE(s->va, h * w, TERM_COLOR);
336         C_MAKE(s->vc, h * w, char);
337
338         /* Make the terrain access arrays */
339         C_MAKE(s->ta, h, TERM_COLOR*);
340         C_MAKE(s->tc, h, char*);
341
342         /* Make the terrain content arrays */
343         C_MAKE(s->vta, h * w, TERM_COLOR);
344         C_MAKE(s->vtc, h * w, char);
345
346
347         /* Prepare the window access arrays */
348         for (y = 0; y < h; y++)
349         {
350                 s->a[y] = s->va + w * y;
351                 s->c[y] = s->vc + w * y;
352
353                 s->ta[y] = s->vta + w * y;
354                 s->tc[y] = s->vtc + w * y;
355         }
356
357         /* Success */
358         return (0);
359 }
360
361
362 /*
363  * Copy a "term_win" from another
364  */
365 static errr term_win_copy(term_win *s, term_win *f, TERM_LEN w, TERM_LEN h)
366 {
367         TERM_LEN x, y;
368
369         /* Copy contents */
370         for (y = 0; y < h; y++)
371         {
372                 TERM_COLOR *f_aa = f->a[y];
373                 char *f_cc = f->c[y];
374
375                 TERM_COLOR *s_aa = s->a[y];
376                 char *s_cc = s->c[y];
377
378                 TERM_COLOR *f_taa = f->ta[y];
379                 char *f_tcc = f->tc[y];
380
381                 TERM_COLOR *s_taa = s->ta[y];
382                 char *s_tcc = s->tc[y];
383
384                 for (x = 0; x < w; x++)
385                 {
386                         *s_aa++ = *f_aa++;
387                         *s_cc++ = *f_cc++;
388
389                         *s_taa++ = *f_taa++;
390                         *s_tcc++ = *f_tcc++;
391                 }
392         }
393
394         /* Copy cursor */
395         s->cx = f->cx;
396         s->cy = f->cy;
397         s->cu = f->cu;
398         s->cv = f->cv;
399
400         /* Success */
401         return (0);
402 }
403
404
405
406 /*** External hooks ***/
407
408
409 /*
410  * Execute the "Term->user_hook" hook, if available (see above).
411  */
412 errr Term_user(int n)
413 {
414         /* Verify the hook */
415         if (!Term->user_hook) return (-1);
416
417         /* Call the hook */
418         return ((*Term->user_hook)(n));
419 }
420
421 /*
422  * Execute the "Term->xtra_hook" hook, if available (see above).
423  */
424 errr Term_xtra(int n, int v)
425 {
426         /* Verify the hook */
427         if (!Term->xtra_hook) return (-1);
428
429         /* Call the hook */
430         return ((*Term->xtra_hook)(n, v));
431 }
432
433
434
435 /*** Fake hooks ***/
436
437
438 /*
439  * Hack -- fake hook for "Term_curs()" (see above)
440  */
441 static errr Term_curs_hack(TERM_LEN x, TERM_LEN y)
442 {
443         /* Unused */
444         (void)x;
445         (void)y;
446
447         return (-1);
448 }
449
450 /*
451  * Hack -- fake hook for "Term_bigcurs()" (see above)
452  */
453 static errr Term_bigcurs_hack(TERM_LEN x, TERM_LEN y)
454 {
455         return (*Term->curs_hook)(x, y);
456 }
457
458 /*
459  * Hack -- fake hook for "Term_wipe()" (see above)
460  */
461 static errr Term_wipe_hack(TERM_LEN x, TERM_LEN y, int n)
462 {
463         /* Unused */
464         (void)x;
465         (void)y;
466         (void)n;
467
468         return (-1);
469 }
470
471 /*
472  * Hack -- fake hook for "Term_text()" (see above)
473  */
474 static errr Term_text_hack(TERM_LEN x, TERM_LEN y, int n, TERM_COLOR a, concptr cp)
475 {
476         /* Unused */
477         (void)x;
478         (void)y;
479         (void)n;
480         (void)a;
481         (void)cp;
482
483         return (-1);
484 }
485
486 /*
487  * Hack -- fake hook for "Term_pict()" (see above)
488  */
489 static errr Term_pict_hack(TERM_LEN x, TERM_LEN y, int n, const TERM_COLOR *ap, concptr cp, const TERM_COLOR *tap, concptr tcp)
490 {
491         /* Unused */
492         (void)x;
493         (void)y;
494         (void)n;
495         (void)ap;
496         (void)cp;
497         (void)tap;
498         (void)tcp;
499
500         return (-1);
501 }
502
503
504
505 /*** Efficient routines ***/
506
507
508 /*
509  * Mentally draw an attr/char at a given location
510  * Assumes given location and values are valid.
511  */
512 void Term_queue_char(TERM_LEN x, TERM_LEN y, TERM_COLOR a, char c, TERM_COLOR ta, char tc)
513 {
514         term_win *scrn = Term->scr; 
515         
516         TERM_COLOR *scr_aa = &scrn->a[y][x];
517         char *scr_cc = &scrn->c[y][x];
518
519         TERM_COLOR *scr_taa = &scrn->ta[y][x];
520         char *scr_tcc = &scrn->tc[y][x];
521
522         /* Hack -- Ignore non-changes */
523         if ((*scr_aa == a) && (*scr_cc == c) &&
524                  (*scr_taa == ta) && (*scr_tcc == tc)) return;
525
526         /* Save the "literal" information */
527         *scr_aa = a;
528         *scr_cc = c;
529
530         *scr_taa = ta;
531         *scr_tcc = tc;
532
533         /* Check for new min/max row info */
534         if (y < Term->y1) Term->y1 = (byte_hack)y;
535         if (y > Term->y2) Term->y2 = (byte_hack)y;
536
537         /* Check for new min/max col info for this row */
538         if (x < Term->x1[y]) Term->x1[y] = (byte_hack)x;
539         if (x > Term->x2[y]) Term->x2[y] = (byte_hack)x;
540
541 #ifdef JP
542         if (((scrn->a[y][x] & AF_BIGTILE2) == AF_BIGTILE2) ||
543             (scrn->a[y][x] & AF_KANJI2))
544 #else
545         if ((scrn->a[y][x] & AF_BIGTILE2) == AF_BIGTILE2)
546 #endif
547                 if ((x - 1) < Term->x1[y]) Term->x1[y]--;
548 }
549
550
551 /*
552  * Bigtile version of Term_queue_char().
553  * If use_bigtile is FALSE, simply call Term_queue_char().
554  * Otherwise, mentally draw a pair of attr/char at a given location.
555  * Assumes given location and values are valid.
556  */
557 void Term_queue_bigchar(TERM_LEN x, TERM_LEN y, TERM_COLOR a, char c, TERM_COLOR ta, char tc)
558 {
559
560 #ifdef JP
561         /*
562          * A table which relates each ascii character to a multibyte
563          * character.
564          *
565          * 「■」は二倍幅豆腐の内部コードに使用。
566          */
567         static char ascii_to_zenkaku[] =
568                 " !”#$%&’()*+,-./"
569                 "0123456789:;<=>?"
570                 "@ABCDEFGHIJKLMNO"
571                 "PQRSTUVWXYZ[\]^_"
572                 "‘abcdefghijklmno"
573                 "pqrstuvwxyz{|}~■";
574 #endif
575
576         byte a2;
577         char c2;
578
579         /* If non bigtile mode, call orginal function */
580         if (!use_bigtile)
581         {
582                 Term_queue_char(x, y, a, c, ta, tc);
583                 return;
584         }
585
586         /* A tile becomes a Bigtile */
587         if ((a & AF_TILE1) && (c & 0x80))
588         {
589                 /* Mark it as a Bigtile */
590                 a2 = AF_BIGTILE2;
591
592                 c2 = -1;
593
594                 /* Ignore non-tile background */
595                 if (!((ta & AF_TILE1) && (tc & 0x80)))
596                 {
597                         ta = 0;
598                         tc = 0;
599                 }
600         }
601
602 #ifdef JP
603         /*
604          * Use a multibyte character instead of a dirty pair of ASCII
605          * characters.
606          */
607         else if (' ' <= c) /* isprint(c) */
608         {
609                 c2 = ascii_to_zenkaku[2 * (c - ' ') + 1];
610                 c = ascii_to_zenkaku[2 * (c - ' ')];
611
612                 /* Mark it as a Kanji */
613                 a2 = a | AF_KANJI2;
614                 a |= AF_KANJI1;
615         }
616 #endif
617
618         else
619         {
620                 /* Dirty pair of ASCII characters */
621                 a2 = TERM_WHITE;
622                 c2 = ' ';
623         }
624
625         /* Display pair of attr/char */
626         Term_queue_char(x, y, a, c, ta, tc);
627         Term_queue_char(x + 1, y, a2, c2, 0, 0);
628 }
629
630
631 /*
632  * Mentally draw a string of attr/chars at a given location
633  *
634  * Assumes given location and values are valid.
635  *
636  * This function is designed to be fast, with no consistancy checking.
637  * It is used to update the map in the game.
638  */
639 void Term_queue_line(TERM_LEN x, TERM_LEN y, int n, TERM_COLOR *a, char *c, TERM_COLOR *ta, char *tc)
640 {
641         term_win *scrn = Term->scr;
642
643         TERM_LEN x1 = -1;
644         TERM_LEN x2 = -1;
645
646         TERM_COLOR *scr_aa = &scrn->a[y][x];
647         char *scr_cc = &scrn->c[y][x];
648
649         TERM_COLOR *scr_taa = &scrn->ta[y][x];
650         char *scr_tcc = &scrn->tc[y][x];
651
652         while (n--)
653         {
654                 /* Hack -- Ignore non-changes */
655                 if ((*scr_aa == *a) && (*scr_cc == *c) &&
656                         (*scr_taa == *ta) && (*scr_tcc == *tc))
657                 {
658                         x++;
659                         a++;
660                         c++;
661                         ta++;
662                         tc++;
663                         scr_aa++;
664                         scr_cc++;
665                         scr_taa++;
666                         scr_tcc++;
667                         continue;
668                 }
669
670                 /* Save the "literal" information */
671                 *scr_taa++ = *ta++;
672                 *scr_tcc++ = *tc++;
673
674                 /* Save the "literal" information */
675                 *scr_aa++ = *a++;
676                 *scr_cc++ = *c++;
677
678                 /* Track minimum changed column */
679                 if (x1 < 0) x1 = x;
680
681                 /* Track maximum changed column */
682                 x2 = x;
683
684                 x++;
685         }
686
687         /* Expand the "change area" as needed */
688         if (x1 >= 0)
689         {
690                 /* Check for new min/max row info */
691                 if (y < Term->y1) Term->y1 = (byte_hack)y;
692                 if (y > Term->y2) Term->y2 = (byte_hack)y;
693
694                 /* Check for new min/max col info in this row */
695                 if (x1 < Term->x1[y]) Term->x1[y] = (byte_hack)x1;
696                 if (x2 > Term->x2[y]) Term->x2[y] = (byte_hack)x2;
697         }
698 }
699
700
701
702 /*
703  * Mentally draw some attr/chars at a given location
704  *
705  * Assumes that (x,y) is a valid location, that the first "n" characters
706  * of the string "s" are all valid (non-zero), and that (x+n-1,y) is also
707  * a valid location, so the first "n" characters of "s" can all be added
708  * starting at (x,y) without causing any illegal operations.
709  */
710 void Term_queue_chars(TERM_LEN x, TERM_LEN y, int n, TERM_COLOR a, concptr s)
711 {
712         TERM_LEN x1 = -1, x2 = -1;
713
714         TERM_COLOR *scr_aa = Term->scr->a[y];
715 #ifdef JP
716         char *scr_cc = Term->scr->c[y];
717
718         TERM_COLOR *scr_taa = Term->scr->ta[y];
719         char *scr_tcc = Term->scr->tc[y];
720 #else
721         char *scr_cc = Term->scr->c[y];
722
723         TERM_COLOR *scr_taa = Term->scr->ta[y];
724         char *scr_tcc = Term->scr->tc[y];
725 #endif
726
727
728 #ifdef JP
729         /* 表示文字なし */
730         if (n == 0 || *s == 0) return;
731         /*
732          * 全角文字の右半分から文字を表示する場合、
733          * 重なった文字の左部分を消去。
734          * 表示開始位置が左端でないと仮定。
735          */
736         if ((scr_aa[x] & AF_KANJI2) && (scr_aa[x] & AF_BIGTILE2) != AF_BIGTILE2)
737         {
738                 scr_cc[x - 1] = ' ';
739                 scr_aa[x - 1] &= AF_KANJIC;
740                 x1 = x2 = x - 1;
741         }
742 #endif
743         /* Queue the attr/chars */
744         for ( ; n; x++, s++, n--)
745         {
746 #ifdef JP
747                 /* 特殊文字としてMSBが立っている可能性がある */
748                 /* その場合attrのMSBも立っているのでこれで識別する */
749 /* check */
750                 if (!(a & AF_TILE1) && iskanji(*s))
751                 {
752                         char nc1 = *s++;
753                         char nc2 = *s;
754
755                         byte na1 = (a | AF_KANJI1);
756                         byte na2 = (a | AF_KANJI2);
757
758                         if((--n == 0) || !nc2) break;
759
760                         if(scr_aa[x++] == na1 && scr_aa[x] == na2 &&
761                            scr_cc[x - 1] == nc1 && scr_cc[x] == nc2 &&
762                            (scr_taa[x - 1] == 0) && (scr_taa[x]==0) &&
763                            (scr_tcc[x - 1] == 0) && (scr_tcc[x]==0)    )
764                                 continue;
765
766                         scr_aa[x - 1] = na1;
767                         scr_aa[x] = na2;
768                         scr_cc[x - 1] = nc1;
769                         scr_cc[x] = nc2;
770
771                         if(x1 < 0) x1 = x - 1;
772                         x2 = x;
773                 }
774                 else
775                 {
776 #endif
777                 TERM_COLOR oa = scr_aa[x];
778                 char oc = scr_cc[x];
779
780                 TERM_COLOR ota = scr_taa[x];
781                 char otc = scr_tcc[x];
782
783                 /* Hack -- Ignore non-changes */
784                 if ((oa == a) && (oc == *s) && (ota == 0) && (otc == 0)) continue;
785
786                 /* Save the "literal" information */
787                 scr_aa[x] = a;
788                 scr_cc[x] = *s;
789
790                 scr_taa[x] = 0;
791                 scr_tcc[x] = 0;
792
793                 /* Note the "range" of window updates */
794                 if (x1 < 0) x1 = x;
795                 x2 = x;
796 #ifdef JP
797         }
798 #endif
799         }
800
801 #ifdef JP
802         /*
803          * 全角文字の左半分で表示を終了する場合、
804          * 重なった文字の右部分を消去。
805          * (条件追加:タイルの1文字目でない事を確かめるように。)
806          */
807         {
808
809                 int w, h;
810                 Term_get_size(&w, &h);
811                 if (x != w && !(scr_aa[x] & AF_TILE1) && (scr_aa[x] & AF_KANJI2))
812                 {
813                         scr_cc[x] = ' ';
814                         scr_aa[x] &= AF_KANJIC;
815                         if (x1 < 0) x1 = x;
816                         x2 = x;
817                 }
818         }
819 #endif
820         /* Expand the "change area" as needed */
821         if (x1 >= 0)
822         {
823                 /* Check for new min/max row info */
824                 if (y < Term->y1) Term->y1 = (byte_hack)y;
825                 if (y > Term->y2) Term->y2 = (byte_hack)y;
826
827                 /* Check for new min/max col info in this row */
828                 if (x1 < Term->x1[y]) Term->x1[y] = (byte_hack)x1;
829                 if (x2 > Term->x2[y]) Term->x2[y] = (byte_hack)x2;
830         }
831 }
832
833
834
835 /*** Refresh routines ***/
836
837
838 /*
839  * Flush a row of the current window (see "Term_fresh")
840  *
841  * Display text using "Term_pict()"
842  */
843 static void Term_fresh_row_pict(TERM_LEN y, TERM_LEN x1, TERM_LEN x2)
844 {
845         TERM_LEN x;
846
847         TERM_COLOR *old_aa = Term->old->a[y];
848         char *old_cc = Term->old->c[y];
849
850         TERM_COLOR *scr_aa = Term->scr->a[y];
851         char *scr_cc = Term->scr->c[y];
852
853         TERM_COLOR *old_taa = Term->old->ta[y];
854         char *old_tcc = Term->old->tc[y];
855
856         TERM_COLOR *scr_taa = Term->scr->ta[y];
857         char *scr_tcc = Term->scr->tc[y];
858
859         TERM_COLOR ota;
860         char otc;
861
862         TERM_COLOR nta;
863         char ntc;
864
865
866         /* Pending length */
867         TERM_LEN fn = 0;
868
869         /* Pending start */
870         TERM_LEN fx = 0;
871
872         TERM_COLOR oa;
873         char oc;
874
875         TERM_COLOR na;
876         char nc;
877
878 #ifdef JP
879         /* 全角文字の2バイト目かどうか */
880         int kanji = 0;
881 #endif
882         /* Scan "modified" columns */
883         for (x = x1; x <= x2; x++)
884         {
885                 /* See what is currently here */
886                 oa = old_aa[x];
887                 oc = old_cc[x];
888
889                 /* See what is desired there */
890                 na = scr_aa[x];
891                 nc = scr_cc[x];
892
893 #ifdef JP
894                 if (kanji)
895                 {
896                         /* 全角文字2バイト目 */
897                         kanji = 0;
898                         old_aa[x] = na;
899                         old_cc[x] = nc;
900                         fn++;
901                         continue;
902                 }
903                 /* 特殊文字としてMSBが立っている可能性がある */
904                 /* その場合attrのMSBも立っているのでこれで識別する */
905 /* check */
906                 kanji = (iskanji(nc) && !(na & AF_TILE1));
907 #endif
908
909                 ota = old_taa[x];
910                 otc = old_tcc[x];
911
912                 nta = scr_taa[x];
913                 ntc = scr_tcc[x];
914
915                 /* Handle unchanged grids */
916 #ifdef JP
917                 if ((na == oa) && (nc == oc) && (nta == ota) && (ntc == otc)
918                     &&(!kanji || (scr_aa[x + 1] == old_aa[x + 1] &&
919                                   scr_cc[x + 1] == old_cc[x + 1] &&
920                                   scr_taa[x + 1] == old_taa[x + 1] &&
921                                   scr_tcc[x + 1] == old_tcc[x + 1])))
922 #else
923                 if ((na == oa) && (nc == oc) && (nta == ota) && (ntc == otc))
924 #endif
925                 {
926                         /* Flush */
927                         if (fn)
928                         {
929                                 /* Draw pending attr/char pairs */
930                                 (void)((*Term->pict_hook)(fx, y, fn,
931                                        &scr_aa[fx], &scr_cc[fx], &scr_taa[fx], &scr_tcc[fx]));
932
933                                 /* Forget */
934                                 fn = 0;
935                         }
936
937 #ifdef JP
938                         /* 全角文字の時は再開位置は+1 */
939                         if(kanji)
940                         {
941                                 x++;
942                                 fx++;
943                                 kanji = 0;
944                         }
945 #endif
946                         /* Skip */
947                         continue;
948                 }
949                 /* Save new contents */
950                 old_aa[x] = na;
951                 old_cc[x] = nc;
952
953                 old_taa[x] = nta;
954                 old_tcc[x] = ntc;
955
956                 /* Restart and Advance */
957                 if (fn++ == 0) fx = x;
958         }
959
960         /* Flush */
961         if (fn)
962         {
963                 /* Draw pending attr/char pairs */
964                 (void)((*Term->pict_hook)(fx, y, fn,
965                         &scr_aa[fx], &scr_cc[fx], &scr_taa[fx], &scr_tcc[fx]));
966         }
967 }
968
969
970
971 /*
972  * Flush a row of the current window (see "Term_fresh")
973  *
974  * Display text using "Term_text()" and "Term_wipe()",
975  * but use "Term_pict()" for high-bit attr/char pairs
976  */
977 static void Term_fresh_row_both(TERM_LEN y, int x1, int x2)
978 {
979         TERM_LEN x;
980
981         TERM_COLOR *old_aa = Term->old->a[y];
982         char *old_cc = Term->old->c[y];
983
984         TERM_COLOR *scr_aa = Term->scr->a[y];
985         char *scr_cc = Term->scr->c[y];
986
987         TERM_COLOR *old_taa = Term->old->ta[y];
988         char *old_tcc = Term->old->tc[y];
989         TERM_COLOR *scr_taa = Term->scr->ta[y];
990         char *scr_tcc = Term->scr->tc[y];
991
992         TERM_COLOR ota;
993         char otc;
994         TERM_COLOR nta;
995         char ntc;
996
997         /* The "always_text" flag */
998         int always_text = Term->always_text;
999
1000         /* Pending length */
1001         int fn = 0;
1002
1003         /* Pending start */
1004         int fx = 0;
1005
1006         /* Pending attr */
1007         byte fa = Term->attr_blank;
1008
1009         TERM_COLOR oa;
1010         char oc;
1011
1012         TERM_COLOR na;
1013         char nc;
1014
1015 #ifdef JP
1016         /* 全角文字の2バイト目かどうか */
1017         int kanji = 0;
1018 #endif
1019         /* Scan "modified" columns */
1020         for (x = x1; x <= x2; x++)
1021         {
1022                 /* See what is currently here */
1023                 oa = old_aa[x];
1024                 oc = old_cc[x];
1025
1026                 /* See what is desired there */
1027                 na = scr_aa[x];
1028                 nc = scr_cc[x];
1029
1030 #ifdef JP
1031                 if (kanji)
1032                 {
1033                         /* 全角文字2バイト目 */
1034                         kanji = 0;
1035                         old_aa[x] = na;
1036                         old_cc[x] = nc;
1037                         fn++;
1038                         continue;
1039                 }
1040                 /* 特殊文字としてMSBが立っている可能性がある */
1041                 /* その場合attrのMSBも立っているのでこれで識別する */
1042 /* check */
1043 /*              kanji = (iskanji(nc));  */
1044                 kanji = (iskanji(nc) && !(na & AF_TILE1));
1045 #endif
1046
1047                 ota = old_taa[x];
1048                 otc = old_tcc[x];
1049
1050                 nta = scr_taa[x];
1051                 ntc = scr_tcc[x];
1052
1053                 /* Handle unchanged grids */
1054 #ifdef JP
1055                 if ((na == oa) && (nc == oc) && (nta == ota) && (ntc == otc)&&
1056                     (!kanji || (scr_aa[x + 1] == old_aa[x + 1] &&
1057                                 scr_cc[x + 1] == old_cc[x + 1] &&
1058                                 scr_taa[x + 1] == old_taa[x + 1] &&
1059                                 scr_tcc[x + 1] == old_tcc[x + 1])))
1060 #else
1061                 if ((na == oa) && (nc == oc) && (nta == ota) && (ntc == otc))
1062 #endif
1063                 {
1064                         /* Flush */
1065                         if (fn)
1066                         {
1067                                 /* Draw pending chars (normal) */
1068                                 if (fa || always_text)
1069                                 {
1070                                         (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
1071                                 }
1072
1073                                 /* Draw pending chars (black) */
1074                                 else
1075                                 {
1076                                         (void)((*Term->wipe_hook)(fx, y, fn));
1077                                 }
1078
1079                                 /* Forget */
1080                                 fn = 0;
1081                         }
1082
1083 #ifdef JP
1084                         /* 全角文字の時は再開位置は+1 */
1085                         if(kanji)
1086                         {
1087                                 x++;
1088                                 fx++;
1089                                 kanji = 0;
1090                         }
1091 #endif
1092                         /* Skip */
1093                         continue;
1094                 }
1095
1096                 /* Save new contents */
1097                 old_aa[x] = na;
1098                 old_cc[x] = nc;
1099
1100                 old_taa[x] = nta;
1101                 old_tcc[x] = ntc;
1102
1103                 /* 2nd byte of bigtile */
1104                 if ((na & AF_BIGTILE2) == AF_BIGTILE2) continue;
1105
1106                 /* Handle high-bit attr/chars */
1107                 if ((na & AF_TILE1) && (nc & 0x80))
1108                 {
1109                         /* Flush */
1110                         if (fn)
1111                         {
1112                                 /* Draw pending chars (normal) */
1113                                 if (fa || always_text)
1114                                 {
1115                                         (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
1116                                 }
1117
1118                                 /* Draw pending chars (black) */
1119                                 else
1120                                 {
1121                                         (void)((*Term->wipe_hook)(fx, y, fn));
1122                                 }
1123
1124                                 /* Forget */
1125                                 fn = 0;
1126                         }
1127
1128                         /* Hack -- Draw the special attr/char pair */
1129                         (void)((*Term->pict_hook)(x, y, 1, &na, &nc, &nta, &ntc));
1130
1131                         /* Skip */
1132                         continue;
1133                 }
1134
1135                 /* Notice new color */
1136 #ifdef JP
1137                 if (fa != (na & AF_KANJIC))
1138 #else
1139                 if (fa != na)
1140 #endif
1141
1142                 {
1143                         /* Flush */
1144                         if (fn)
1145                         {
1146                                 /* Draw the pending chars */
1147                                 if (fa || always_text)
1148                                 {
1149                                         (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
1150                                 }
1151
1152                                 /* Hack -- Erase "leading" spaces */
1153                                 else
1154                                 {
1155                                         (void)((*Term->wipe_hook)(fx, y, fn));
1156                                 }
1157
1158                                 /* Forget */
1159                                 fn = 0;
1160                         }
1161
1162                         /* Save the new color */
1163 #ifdef JP
1164                         fa = (na & AF_KANJIC);
1165 #else
1166                         fa = na;
1167 #endif
1168
1169                 }
1170
1171                 /* Restart and Advance */
1172                 if (fn++ == 0) fx = x;
1173         }
1174
1175         /* Flush */
1176         if (fn)
1177         {
1178                 /* Draw pending chars (normal) */
1179                 if (fa || always_text)
1180                 {
1181                         (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
1182                 }
1183
1184                 /* Draw pending chars (black) */
1185                 else
1186                 {
1187                         (void)((*Term->wipe_hook)(fx, y, fn));
1188                 }
1189         }
1190 }
1191
1192
1193 /*
1194  * Flush a row of the current window (see "Term_fresh")
1195  *
1196  * Display text using "Term_text()" and "Term_wipe()"
1197  */
1198 static void Term_fresh_row_text(TERM_LEN y, TERM_LEN x1, TERM_LEN x2)
1199 {
1200         TERM_LEN x;
1201
1202         TERM_COLOR *old_aa = Term->old->a[y];
1203         char *old_cc = Term->old->c[y];
1204
1205         TERM_COLOR *scr_aa = Term->scr->a[y];
1206         char *scr_cc = Term->scr->c[y];
1207
1208         /* The "always_text" flag */
1209         int always_text = Term->always_text;
1210
1211         /* Pending length */
1212         int fn = 0;
1213
1214         /* Pending start */
1215         int fx = 0;
1216
1217         /* Pending attr */
1218         byte fa = Term->attr_blank;
1219
1220         TERM_COLOR oa;
1221         char oc;
1222
1223         TERM_COLOR na;
1224         char nc;
1225
1226 #ifdef JP
1227         /* 全角文字の2バイト目かどうか */
1228         int kanji = 0;
1229
1230         for (x = 0; x < x1; x++)
1231                 if (!(old_aa[x] & AF_TILE1) && iskanji(old_cc[x]))
1232                 {
1233                         if (x == x1 - 1)
1234                         {
1235                                 x1--;
1236                                 break;
1237                         }
1238                         else
1239                                 x++;
1240                 }
1241 #endif
1242         /* Scan "modified" columns */
1243         for (x = x1; x <= x2; x++)
1244         {
1245                 /* See what is currently here */
1246                 oa = old_aa[x];
1247                 oc = old_cc[x];
1248
1249                 /* See what is desired there */
1250                 na = scr_aa[x];
1251                 nc = scr_cc[x];
1252
1253 #ifdef JP
1254                 if (kanji)
1255                 {
1256                         /* 全角文字2バイト目 */
1257                         kanji = 0;
1258                         old_aa[x] = na;
1259                         old_cc[x] = nc;
1260                         fn++;
1261                         continue;
1262                 }
1263                 /* 特殊文字としてMSBが立っている可能性がある */
1264                 /* その場合attrのMSBも立っているのでこれで識別する */
1265 /* check */
1266                 kanji = (iskanji(nc) && !(na & AF_TILE1));
1267 #endif
1268                 /* Handle unchanged grids */
1269 #ifdef JP
1270                 if ((na == oa) && (nc == oc) &&
1271                     (!kanji || (scr_aa[x + 1] == old_aa[x + 1] &&
1272                                 scr_cc[x + 1] == old_cc[x + 1])))
1273 #else
1274                 if ((na == oa) && (nc == oc))
1275 #endif
1276
1277                 {
1278                         /* Flush */
1279                         if (fn)
1280                         {
1281                                 /* Draw pending chars (normal) */
1282                                 if (fa || always_text)
1283                                 {
1284                                         (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
1285                                 }
1286
1287                                 /* Draw pending chars (black) */
1288                                 else
1289                                 {
1290                                         (void)((*Term->wipe_hook)(fx, y, fn));
1291                                 }
1292
1293                                 /* Forget */
1294                                 fn = 0;
1295                         }
1296
1297 #ifdef JP
1298                         /* 全角文字の時は再開位置は+1 */
1299                         if(kanji)
1300                         {
1301                                 x++;
1302                                 fx++;
1303                                 kanji = 0;
1304                         }
1305 #endif
1306                         /* Skip */
1307                         continue;
1308                 }
1309
1310                 /* Save new contents */
1311                 old_aa[x] = na;
1312                 old_cc[x] = nc;
1313
1314                 /* Notice new color */
1315 #ifdef JP
1316                 if (fa != (na & AF_KANJIC))
1317 #else
1318                 if (fa != na)
1319 #endif
1320
1321                 {
1322                         /* Flush */
1323                         if (fn)
1324                         {
1325                                 /* Draw the pending chars */
1326                                 if (fa || always_text)
1327                                 {
1328                                         (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
1329                                 }
1330
1331                                 /* Hack -- Erase "leading" spaces */
1332                                 else
1333                                 {
1334                                         (void)((*Term->wipe_hook)(fx, y, fn));
1335                                 }
1336
1337                                 /* Forget */
1338                                 fn = 0;
1339                         }
1340
1341                         /* Save the new color */
1342 #ifdef JP
1343                         fa = (na & AF_KANJIC);
1344 #else
1345                         fa = na;
1346 #endif
1347
1348                 }
1349
1350                 /* Restart and Advance */
1351                 if (fn++ == 0) fx = x;
1352         }
1353
1354         /* Flush */
1355         if (fn)
1356         {
1357                 /* Draw pending chars (normal) */
1358                 if (fa || always_text)
1359                 {
1360                         (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
1361                 }
1362
1363                 /* Draw pending chars (black) */
1364                 else
1365                 {
1366                         (void)((*Term->wipe_hook)(fx, y, fn));
1367                 }
1368         }
1369 }
1370
1371
1372
1373
1374
1375 /*
1376  * Actually perform all requested changes to the window
1377  *
1378  * If absolutely nothing has changed, not even temporarily, or if the
1379  * current "Term" is not mapped, then this function will return 1 and
1380  * do absolutely nothing.
1381  *
1382  * Note that when "soft_cursor" is true, we erase the cursor (if needed)
1383  * whenever anything has changed, and redraw it (if needed) after all of
1384  * the screen updates are complete.  This will induce a small amount of
1385  * "cursor flicker" but only when the screen has been updated.  If the
1386  * screen is updated and then restored, you may still get this flicker.
1387  *
1388  * When "soft_cursor" is not true, we make the cursor invisible before
1389  * doing anything else if it is supposed to be invisible by the time we
1390  * are done, and we make it visible after moving it to its final location
1391  * after all of the screen updates are complete.
1392  *
1393  * Note that "Term_xtra(TERM_XTRA_CLEAR,0)" must erase the entire screen,
1394  * including the cursor, if needed, and may place the cursor anywhere.
1395  *
1396  * Note that "Term_xtra(TERM_XTRA_FROSH,y)" will be always be called
1397  * after any row "y" has been "flushed", unless the "Term->never_frosh"
1398  * flag is set, and "Term_xtra(TERM_XTRA_FRESH,0)" will be called after
1399  * all of the rows have been "flushed".
1400  *
1401  * Note the use of three different functions to handle the actual flush,
1402  * based on the settings of the "Term->always_pict" and "Term->higher_pict"
1403  * flags (see below).
1404  *
1405  * The three helper functions (above) work by collecting similar adjacent
1406  * grids into stripes, and then sending each stripe to "Term->pict_hook",
1407  * "Term->text_hook", or "Term->wipe_hook", based on the settings of the
1408  * "Term->always_pict" and "Term->higher_pict" flags, which select which
1409  * of the helper functions to call to flush each row.
1410  *
1411  * The helper functions currently "skip" any grids which already contain
1412  * the desired contents.  This may or may not be the best method, especially
1413  * when the desired content fits nicely into the current stripe.  For example,
1414  * it might be better to go ahead and queue them while allowed, but keep a
1415  * count of the "trailing skipables", then, when time to flush, or when a
1416  * "non skippable" is found, force a flush if there are too many skippables.
1417  *
1418  * Perhaps an "initialization" stage, where the "text" (and "attr")
1419  * buffers are "filled" with information, converting "blanks" into
1420  * a convenient representation, and marking "skips" with "zero chars",
1421  * and then some "processing" is done to determine which chars to skip.
1422  *
1423  * Currently, the helper functions are optimal for systems which prefer
1424  * to "print a char + move a char + print a char" to "print three chars",
1425  * and for applications that do a lot of "detailed" color printing.
1426  *
1427  * In the two "queue" functions, total "non-changes" are "pre-skipped".
1428  * The helper functions must also handle situations in which the contents
1429  * of a grid are changed, but then changed back to the original value,
1430  * and situations in which two grids in the same row are changed, but
1431  * the grids between them are unchanged.
1432  *
1433  * If the "Term->always_pict" flag is set, then "Term_fresh_row_pict()"
1434  * will be used instead of "Term_fresh_row_text()".  This allows all the
1435  * modified grids to be collected into stripes of attr/char pairs, which
1436  * are then sent to the "Term->pict_hook" hook, which can draw these pairs
1437  * in whatever way it would like.
1438  *
1439  * If the "Term->higher_pict" flag is set, then "Term_fresh_row_both()"
1440  * will be used instead of "Term_fresh_row_text()".  This allows all the
1441  * "special" attr/char pairs (in which both the attr and char have the
1442  * high-bit set) to be sent (one pair at a time) to the "Term->pict_hook"
1443  * hook, which can draw these pairs in whatever way it would like.
1444  *
1445  * Normally, the "Term_wipe()" function is used only to display "blanks"
1446  * that were induced by "Term_clear()" or "Term_erase()", and then only
1447  * if the "attr_blank" and "char_blank" fields have not been redefined
1448  * to use "white space" instead of the default "black space".  Actually,
1449  * the "Term_wipe()" function is used to display all "black" text, such
1450  * as the default "spaces" created by "Term_clear()" and "Term_erase()".
1451  *
1452  * Note that the "Term->always_text" flag will disable the use of the
1453  * "Term_wipe()" function hook entirely, and force all text, even text
1454  * drawn in the color "black", to be explicitly drawn.  This is useful
1455  * for machines which implement "Term_wipe()" by just drawing spaces.
1456  *
1457  * Note that the "Term->always_pict" flag will disable the use of the
1458  * "Term_wipe()" function entirely, and force everything, even text
1459  * drawn in the attr "black", to be explicitly drawn.
1460  *
1461  * Note that if no "black" text is ever drawn, and if "attr_blank" is
1462  * not "zero", then the "Term_wipe" hook will never be used, even if
1463  * the "Term->always_text" flag is not set.
1464  *
1465  * This function does nothing unless the "Term" is "mapped", which allows
1466  * certain systems to optimize the handling of "closed" windows.
1467  *
1468  * On systems with a "soft" cursor, we must explicitly erase the cursor
1469  * before flushing the output, if needed, to prevent a "jumpy" refresh.
1470  * The actual method for this is horrible, but there is very little that
1471  * we can do to simplify it efficiently.  
1472  *
1473  * On systems with a "hard" cursor, we will "hide" the cursor before
1474  * flushing the output, if needed, to avoid a "flickery" refresh.  It
1475  * would be nice to *always* hide the cursor during the refresh, but
1476  * this might be expensive (and/or ugly) on some machines.
1477  *
1478  * The "Term->icky_corner" flag is used to avoid calling "Term_wipe()"
1479  * or "Term_pict()" or "Term_text()" on the bottom right corner of the
1480  * window, which might induce "scrolling" or other nasty stuff on old
1481  * dumb terminals.  This flag is handled very efficiently.  We assume
1482  * that the "Term_curs()" call will prevent placing the cursor in the
1483  * corner, if needed, though I doubt such placement is ever a problem.
1484  * Currently, the use of "Term->icky_corner" and "Term->soft_cursor"
1485  * together may result in undefined behavior.
1486  */
1487 errr Term_fresh(void)
1488 {
1489         TERM_LEN x, y;
1490
1491         int w = Term->wid;
1492         int h = Term->hgt;
1493
1494         int y1 = Term->y1;
1495         int y2 = Term->y2;
1496
1497         term_win *old = Term->old;
1498         term_win *scr = Term->scr;
1499         
1500         /* Before initialize (Advice from Mr.shimitei)*/
1501         if (!old || !scr) return (1);
1502
1503         /* Do nothing unless "mapped" */
1504         if (!Term->mapped_flag) return (1);
1505
1506
1507         /* Trivial Refresh */
1508         if ((y1 > y2) &&
1509             (scr->cu == old->cu) &&
1510             (scr->cv == old->cv) &&
1511             (scr->cx == old->cx) &&
1512             (scr->cy == old->cy) &&
1513             !(Term->total_erase))
1514         {
1515                 /* Nothing */
1516                 return (1);
1517         }
1518
1519
1520         /* Handle "total erase" */
1521         if (Term->total_erase)
1522         {
1523                 byte na = Term->attr_blank;
1524                 char nc = Term->char_blank;
1525
1526                 /* Physically erase the entire window */
1527                 Term_xtra(TERM_XTRA_CLEAR, 0);
1528
1529                 /* Hack -- clear all "cursor" data */
1530                 old->cv = old->cu = old->cx = old->cy = 0;
1531
1532                 /* Wipe each row */
1533                 for (y = 0; y < h; y++)
1534                 {
1535                         TERM_COLOR *aa = old->a[y];
1536                         char *cc = old->c[y];
1537
1538                         TERM_COLOR *taa = old->ta[y];
1539                         char *tcc = old->tc[y];
1540
1541
1542                         /* Wipe each column */
1543                         for (x = 0; x < w; x++)
1544                         {
1545                                 /* Wipe each grid */
1546                                 *aa++ = na;
1547                                 *cc++ = nc;
1548
1549                                 *taa++ = na;
1550                                 *tcc++ = nc;
1551                         }
1552                 }
1553
1554                 /* Redraw every row */
1555                 Term->y1 = y1 = 0;
1556                 Term->y2 = y2 = h - 1;
1557
1558                 /* Redraw every column */
1559                 for (y = 0; y < h; y++)
1560                 {
1561                         Term->x1[y] = 0;
1562                         Term->x2[y] = w - 1;
1563                 }
1564
1565                 /* Forget "total erase" */
1566                 Term->total_erase = FALSE;
1567         }
1568
1569
1570         /* Cursor update -- Erase old Cursor */
1571         if (Term->soft_cursor)
1572         {
1573                 /* Cursor was visible */
1574                 if (!old->cu && old->cv)
1575                 {
1576                         int csize = 1;
1577                         TERM_LEN tx = old->cx;
1578                         TERM_LEN ty = old->cy;
1579
1580                         TERM_COLOR *old_aa = old->a[ty];
1581                         char *old_cc = old->c[ty];
1582
1583                         TERM_COLOR *old_taa = old->ta[ty];
1584                         char *old_tcc = old->tc[ty];
1585
1586                         TERM_COLOR ota = old_taa[tx];
1587                         char otc = old_tcc[tx];
1588
1589 #ifdef JP
1590                         if (tx + 1 < Term->wid && !(old_aa[tx] & AF_TILE1)
1591                             && iskanji(old_cc[tx]))
1592                                 csize = 2;
1593 #endif
1594                         /* Hack -- use "Term_pict()" always */
1595                         if (Term->always_pict)
1596                         {
1597                                 (void)((*Term->pict_hook)(tx, ty, csize, &old_aa[tx], &old_cc[tx], &ota, &otc));
1598                         }
1599
1600                         /* Hack -- use "Term_pict()" sometimes */
1601                         else if (Term->higher_pict && (old_aa[tx] & AF_TILE1) && (old_cc[tx] & 0x80))
1602                         {
1603                                 (void)((*Term->pict_hook)(tx, ty, 1, &old_aa[tx], &old_cc[tx], &ota, &otc));
1604                         }
1605                         
1606                         /*
1607                          * Hack -- restore the actual character
1608                          * 元の文字の描画範囲がカーソルより小さいと、
1609                          * 上書きされなかった部分がゴミとして残る。
1610                          * wipe_hook でカーソルを消去して text_hook で書き直す。
1611                          */
1612                         else if (old_aa[tx] || Term->always_text)
1613                         {
1614                                 (void)((*Term->wipe_hook)(tx, ty, 1));
1615                                 (void)((*Term->text_hook)(tx, ty, csize, (unsigned char) (old_aa[tx] & 0xf), &old_cc[tx]));
1616                         }
1617
1618                         /* Hack -- erase the grid */
1619                         else
1620                         {
1621                                 (void)((*Term->wipe_hook)(tx, ty, 1));
1622                         }
1623                 }
1624         }
1625
1626         /* Cursor Update -- Erase old Cursor */
1627         else
1628         {
1629                 /* Cursor will be invisible */
1630                 if (scr->cu || !scr->cv)
1631                 {
1632                         /* Make the cursor invisible */
1633                         Term_xtra(TERM_XTRA_SHAPE, 0);
1634                 }
1635         }
1636
1637
1638         /* Something to update */
1639         if (y1 <= y2)
1640         {
1641                 /* Handle "icky corner" */
1642                 if (Term->icky_corner)
1643                 {
1644                         /* Avoid the corner */
1645                         if (y2 >= h - 1)
1646                         {
1647                                 /* Avoid the corner */
1648                                 if (Term->x2[h - 1] > w - 2)
1649                                 {
1650                                         /* Avoid the corner */
1651                                         Term->x2[h - 1] = w - 2;
1652                                 }
1653                         }
1654                 }
1655
1656
1657                 /* Scan the "modified" rows */
1658                 for (y = y1; y <= y2; ++y)
1659                 {
1660                         TERM_LEN x1 = Term->x1[y];
1661                         TERM_LEN x2 = Term->x2[y];
1662
1663                         /* Flush each "modified" row */
1664                         if (x1 <= x2)
1665                         {
1666                                 /* Always use "Term_pict()" */
1667                                 if (Term->always_pict)
1668                                 {
1669                                         /* Flush the row */
1670                                         Term_fresh_row_pict(y, x1, x2);
1671                                 }
1672
1673                                 /* Sometimes use "Term_pict()" */
1674                                 else if (Term->higher_pict)
1675                                 {
1676                                         /* Flush the row */
1677                                         Term_fresh_row_both(y, x1, x2);
1678                                 }
1679
1680                                 /* Never use "Term_pict()" */
1681                                 else
1682                                 {
1683                                         /* Flush the row */
1684                                         Term_fresh_row_text(y, x1, x2);
1685                                 }
1686
1687                                 /* This row is all done */
1688                                 Term->x1[y] = (byte_hack)w;
1689                                 Term->x2[y] = 0;
1690
1691                                 /* Hack -- Flush that row (if allowed) */
1692                                 if (!Term->never_frosh) Term_xtra(TERM_XTRA_FROSH, y);
1693                         }
1694                 }
1695
1696                 /* No rows are invalid */
1697                 Term->y1 = (byte_hack)h;
1698                 Term->y2 = 0;
1699         }
1700
1701
1702         /* Cursor update -- Show new Cursor */
1703         if (Term->soft_cursor)
1704         {
1705                 /* Draw the cursor */
1706                 if (!scr->cu && scr->cv)
1707                 {
1708 #ifdef JP
1709                         if ((scr->cx + 1 < w) &&
1710                             ((old->a[scr->cy][scr->cx + 1] & AF_BIGTILE2) == AF_BIGTILE2 ||
1711                              (!(old->a[scr->cy][scr->cx] & AF_TILE1) &&
1712                               iskanji(old->c[scr->cy][scr->cx]))))
1713 #else
1714                         if ((scr->cx + 1 < w) && (old->a[scr->cy][scr->cx + 1] & AF_BIGTILE2) == AF_BIGTILE2)
1715 #endif
1716                         {
1717                                 /* Double width cursor for the Bigtile mode */
1718                                 (void)((*Term->bigcurs_hook)(scr->cx, scr->cy));
1719                         }
1720                         else
1721                         {
1722                                 /* Call the cursor display routine */
1723                                 (void)((*Term->curs_hook)(scr->cx, scr->cy));
1724                         }
1725                 }
1726         }
1727
1728         /* Cursor Update -- Show new Cursor */
1729         else
1730         {
1731                 /* The cursor is useless, hide it */
1732                 if (scr->cu)
1733                 {
1734                         /* Paranoia -- Put the cursor NEAR where it belongs */
1735                         (void)((*Term->curs_hook)(w - 1, scr->cy));
1736
1737                         /* Make the cursor invisible */
1738                         /* Term_xtra(TERM_XTRA_SHAPE, 0); */
1739                 }
1740
1741                 /* The cursor is invisible, hide it */
1742                 else if (!scr->cv)
1743                 {
1744                         /* Paranoia -- Put the cursor where it belongs */
1745                         (void)((*Term->curs_hook)(scr->cx, scr->cy));
1746
1747                         /* Make the cursor invisible */
1748                         /* Term_xtra(TERM_XTRA_SHAPE, 0); */
1749                 }
1750
1751                 /* The cursor is visible, display it correctly */
1752                 else
1753                 {
1754                         /* Put the cursor where it belongs */
1755                         (void)((*Term->curs_hook)(scr->cx, scr->cy));
1756
1757                         /* Make the cursor visible */
1758                         Term_xtra(TERM_XTRA_SHAPE, 1);
1759                 }
1760         }
1761
1762
1763         /* Save the "cursor state" */
1764         old->cu = scr->cu;
1765         old->cv = scr->cv;
1766         old->cx = scr->cx;
1767         old->cy = scr->cy;
1768
1769
1770         /* Actually flush the output */
1771         Term_xtra(TERM_XTRA_FRESH, 0);
1772
1773
1774         /* Success */
1775         return (0);
1776 }
1777
1778
1779
1780 /*** Output routines ***/
1781
1782
1783 /*
1784  * Set the cursor visibility
1785  */
1786 errr Term_set_cursor(int v)
1787 {
1788         /* Already done */
1789         if (Term->scr->cv == v) return (1);
1790
1791         /* Change */
1792         Term->scr->cv = (bool_hack)v;
1793
1794         /* Success */
1795         return (0);
1796 }
1797
1798
1799 /*
1800  * Place the cursor at a given location
1801  *
1802  * Note -- "illegal" requests do not move the cursor.
1803  */
1804 errr Term_gotoxy(TERM_LEN x, TERM_LEN y)
1805 {
1806         int w = Term->wid;
1807         int h = Term->hgt;
1808
1809         /* Verify */
1810         if ((x < 0) || (x >= w)) return (-1);
1811         if ((y < 0) || (y >= h)) return (-1);
1812
1813         /* Remember the cursor */
1814         Term->scr->cx = (byte_hack)x;
1815         Term->scr->cy = (byte_hack)y;
1816
1817         /* The cursor is not useless */
1818         Term->scr->cu = 0;
1819
1820         /* Success */
1821         return (0);
1822 }
1823
1824
1825 /*
1826  * At a given location, place an attr/char
1827  * Do not change the cursor position
1828  * No visual changes until "Term_fresh()".
1829  */
1830 errr Term_draw(TERM_LEN x, TERM_LEN y, TERM_COLOR a, char c)
1831 {
1832         int w = Term->wid;
1833         int h = Term->hgt;
1834
1835         /* Verify location */
1836         if ((x < 0) || (x >= w)) return (-1);
1837         if ((y < 0) || (y >= h)) return (-1);
1838
1839         /* Paranoia -- illegal char */
1840         if (!c) return (-2);
1841
1842         /* Queue it for later */
1843         Term_queue_char(x, y, a, c, 0, 0);
1844
1845         /* Success */
1846         return (0);
1847 }
1848
1849
1850 /*
1851  * Using the given attr, add the given char at the cursor.
1852  *
1853  * We return "-2" if the character is "illegal". XXX XXX
1854  *
1855  * We return "-1" if the cursor is currently unusable.
1856  *
1857  * We queue the given attr/char for display at the current
1858  * cursor location, and advance the cursor to the right,
1859  * marking it as unuable and returning "1" if it leaves
1860  * the screen, and otherwise returning "0".
1861  *
1862  * So when this function, or the following one, return a
1863  * positive value, future calls to either function will
1864  * return negative ones.
1865  */
1866 errr Term_addch(TERM_COLOR a, char c)
1867 {
1868         TERM_LEN w = Term->wid;
1869
1870         /* Handle "unusable" cursor */
1871         if (Term->scr->cu) return (-1);
1872
1873         /* Paranoia -- no illegal chars */
1874         if (!c) return (-2);
1875
1876         /* Queue the given character for display */
1877         Term_queue_char(Term->scr->cx, Term->scr->cy, a, c, 0, 0);
1878
1879         /* Advance the cursor */
1880         Term->scr->cx++;
1881
1882         /* Success */
1883         if (Term->scr->cx < w) return (0);
1884
1885         /* Note "Useless" cursor */
1886         Term->scr->cu = 1;
1887
1888         /* Note "Useless" cursor */
1889         return (1);
1890 }
1891
1892
1893 /*
1894  * Bigtile version of Term_addch().
1895  *
1896  * If use_bigtile is FALSE, simply call Term_addch() .
1897  *
1898  * Otherwise, queue a pair of attr/char for display at the current
1899  * cursor location, and advance the cursor to the right by two.
1900  */
1901 errr Term_add_bigch(TERM_COLOR a, char c)
1902 {
1903         if (!use_bigtile) return Term_addch(a, c);
1904
1905         /* Handle "unusable" cursor */
1906         if (Term->scr->cu) return (-1);
1907
1908         /* Paranoia -- no illegal chars */
1909         if (!c) return (-2);
1910
1911         /* Queue the given character for display */
1912         Term_queue_bigchar(Term->scr->cx, Term->scr->cy, a, c, 0, 0);
1913
1914         /* Advance the cursor */
1915         Term->scr->cx += 2;
1916
1917         /* Success */
1918         if (Term->scr->cx < Term->wid) return (0);
1919
1920         /* Note "Useless" cursor */
1921         Term->scr->cu = 1;
1922
1923         /* Note "Useless" cursor */
1924         return (1);
1925 }
1926
1927
1928 /*
1929  * At the current location, using an attr, add a string
1930  *
1931  * We also take a length "n", using negative values to imply
1932  * the largest possible value, and then we use the minimum of
1933  * this length and the "actual" length of the string as the
1934  * actual number of characters to attempt to display, never
1935  * displaying more characters than will actually fit, since
1936  * we do NOT attempt to "wrap" the cursor at the screen edge.
1937  *
1938  * We return "-1" if the cursor is currently unusable.
1939  * We return "N" if we were "only" able to write "N" chars,
1940  * even if all of the given characters fit on the screen,
1941  * and mark the cursor as unusable for future attempts.
1942  *
1943  * So when this function, or the preceding one, return a
1944  * positive value, future calls to either function will
1945  * return negative ones.
1946  */
1947 errr Term_addstr(int n, TERM_COLOR a, concptr s)
1948 {
1949         int k;
1950         TERM_LEN w = Term->wid;
1951         errr res = 0;
1952
1953         /* Handle "unusable" cursor */
1954         if (Term->scr->cu) return (-1);
1955
1956         /* Obtain maximal length */
1957         k = (n < 0) ? (w + 1) : n;
1958
1959         /* Obtain the usable string length */
1960         for (n = 0; (n < k) && s[n]; n++) /* loop */;
1961
1962         /* React to reaching the edge of the screen */
1963         if (Term->scr->cx + n >= w) res = n = w - Term->scr->cx;
1964
1965         /* Queue the first "n" characters for display */
1966         Term_queue_chars(Term->scr->cx, Term->scr->cy, n, a, s);
1967
1968         /* Advance the cursor */
1969         Term->scr->cx += (byte_hack)n;
1970
1971         /* Hack -- Notice "Useless" cursor */
1972         if (res) Term->scr->cu = 1;
1973
1974         /* Success (usually) */
1975         return (res);
1976 }
1977
1978
1979 /*
1980  * Move to a location and, using an attr, add a char
1981  */
1982 errr Term_putch(TERM_LEN x, TERM_LEN y, TERM_COLOR a, char c)
1983 {
1984         errr res;
1985
1986         /* Move first */
1987         if ((res = Term_gotoxy(x, y)) != 0) return (res);
1988
1989         /* Then add the char */
1990         if ((res = Term_addch(a, c)) != 0) return (res);
1991
1992         /* Success */
1993         return (0);
1994 }
1995
1996
1997 /*
1998  * Move to a location and, using an attr, add a string
1999  */
2000 errr Term_putstr(TERM_LEN x, TERM_LEN y, int n, TERM_COLOR a, concptr s)
2001 {
2002         errr res;
2003
2004         /* Move first */
2005         if ((res = Term_gotoxy(x, y)) != 0) return (res);
2006
2007         /* Then add the string */
2008         if ((res = Term_addstr(n, a, s)) != 0) return (res);
2009
2010         /* Success */
2011         return (0);
2012 }
2013
2014 #ifdef JP
2015 /*
2016  * Move to a location and, using an attr, add a string vertically
2017  */
2018 errr Term_putstr_v(TERM_LEN x, TERM_LEN y, int n, byte a, concptr s)
2019 {
2020         errr res;
2021         int i;
2022         int y0 = y;
2023
2024
2025         for (i = 0; i < n && s[i] != 0; i++)
2026         {
2027           /* Move first */
2028           if ((res = Term_gotoxy(x, y0)) != 0) return (res);
2029
2030           if (iskanji(s[i]))
2031           {
2032             if ((res = Term_addstr(2, a, &s[i])) != 0) return (res);
2033             i++;
2034             y0++;
2035             if (s[i] == 0) break;
2036           } else {
2037             if ((res = Term_addstr(1, a, &s[i])) != 0) return (res);
2038             y0++;
2039           }
2040         }
2041
2042         /* Success */
2043         return (0);
2044 }
2045 #endif
2046
2047 /*
2048  * Place cursor at (x,y), and clear the next "n" chars
2049  */
2050 errr Term_erase(TERM_LEN x, TERM_LEN y, int n)
2051 {
2052         int i;
2053
2054         TERM_LEN w = Term->wid;
2055         /* int h = Term->hgt; */
2056
2057         TERM_LEN x1 = -1;
2058         TERM_LEN x2 = -1;
2059
2060         int na = Term->attr_blank;
2061         int nc = Term->char_blank;
2062
2063         TERM_COLOR *scr_aa;
2064         char *scr_cc;
2065
2066         TERM_COLOR *scr_taa;
2067         char *scr_tcc;
2068
2069         /* Place cursor */
2070         if (Term_gotoxy(x, y)) return (-1);
2071
2072         /* Force legal size */
2073         if (x + n > w) n = w - x;
2074
2075         /* Fast access */
2076         scr_aa = Term->scr->a[y];
2077         scr_cc = Term->scr->c[y];
2078
2079         scr_taa = Term->scr->ta[y];
2080         scr_tcc = Term->scr->tc[y];
2081
2082 #ifdef JP
2083         /*
2084          * 全角文字の右半分から文字を表示する場合、
2085          * 重なった文字の左部分を消去。
2086          */
2087         if (n > 0 && (((scr_aa[x] & AF_KANJI2) && !(scr_aa[x] & AF_TILE1))
2088                       || (scr_aa[x] & AF_BIGTILE2) == AF_BIGTILE2))
2089 #else
2090         if (n > 0 && (scr_aa[x] & AF_BIGTILE2) == AF_BIGTILE2)
2091 #endif
2092         {
2093                 x--;
2094                 n++;
2095         }
2096
2097         /* Scan every column */
2098         for (i = 0; i < n; i++, x++)
2099         {
2100                 int oa = scr_aa[x];
2101                 int oc = scr_cc[x];
2102
2103                 /* Hack -- Ignore "non-changes" */
2104                 if ((oa == na) && (oc == nc)) continue;
2105
2106 #ifdef JP
2107                 /*
2108                  * 全角文字の左半分で表示を終了する場合、
2109                  * 重なった文字の右部分を消去。
2110                  *
2111                  * 2001/04/29 -- Habu
2112                  * 行の右端の場合はこの処理をしないように修正。
2113                  */
2114                 if ((oa & AF_KANJI1) && (i + 1) == n && x != w - 1)
2115                         n++;
2116 #endif
2117                 /* Save the "literal" information */
2118                 scr_aa[x] = (byte_hack)na;
2119                 scr_cc[x] = (char)nc;
2120
2121                 scr_taa[x] = 0;
2122                 scr_tcc[x] = 0;
2123
2124                 /* Track minimum changed column */
2125                 if (x1 < 0) x1 = x;
2126
2127                 /* Track maximum changed column */
2128                 x2 = x;
2129         }
2130
2131         /* Expand the "change area" as needed */
2132         if (x1 >= 0)
2133         {
2134                 /* Check for new min/max row info */
2135                 if (y < Term->y1) Term->y1 = (byte_hack)y;
2136                 if (y > Term->y2) Term->y2 = (byte_hack)y;
2137
2138                 /* Check for new min/max col info in this row */
2139                 if (x1 < Term->x1[y]) Term->x1[y] = (byte_hack)x1;
2140                 if (x2 > Term->x2[y]) Term->x2[y] = (byte_hack)x2;
2141         }
2142
2143         /* Success */
2144         return (0);
2145 }
2146
2147
2148 /*
2149  * Clear the entire window, and move to the top left corner
2150  *
2151  * Note the use of the special "total_erase" code
2152  */
2153 errr Term_clear(void)
2154 {
2155         TERM_LEN x, y;
2156
2157         TERM_LEN w = Term->wid;
2158         TERM_LEN h = Term->hgt;
2159
2160         TERM_COLOR na = Term->attr_blank;
2161         char nc = Term->char_blank;
2162
2163         /* Cursor usable */
2164         Term->scr->cu = 0;
2165
2166         /* Cursor to the top left */
2167         Term->scr->cx = Term->scr->cy = 0;
2168
2169         /* Wipe each row */
2170         for (y = 0; y < h; y++)
2171         {
2172                 TERM_COLOR *scr_aa = Term->scr->a[y];
2173                 char *scr_cc = Term->scr->c[y];
2174
2175                 TERM_COLOR *scr_taa = Term->scr->ta[y];
2176                 char *scr_tcc = Term->scr->tc[y];
2177
2178                 /* Wipe each column */
2179                 for (x = 0; x < w; x++)
2180                 {
2181                         scr_aa[x] = na;
2182                         scr_cc[x] = nc;
2183
2184                         scr_taa[x] = 0;
2185                         scr_tcc[x] = 0;
2186                 }
2187
2188                 /* This row has changed */
2189                 Term->x1[y] = 0;
2190                 Term->x2[y] = w - 1;
2191         }
2192
2193         /* Every row has changed */
2194         Term->y1 = 0;
2195         Term->y2 = h - 1;
2196
2197         /* Force "total erase" */
2198         Term->total_erase = TRUE;
2199
2200         /* Success */
2201         return (0);
2202 }
2203
2204
2205
2206
2207
2208 /*
2209  * Redraw (and refresh) the whole window.
2210  */
2211 errr Term_redraw(void)
2212 {
2213         /* Force "total erase" */
2214         Term->total_erase = TRUE;
2215
2216         /* Hack -- Refresh */
2217         Term_fresh();
2218
2219         /* Success */
2220         return (0);
2221 }
2222
2223
2224 /*
2225  * Redraw part of a window.
2226  */
2227 errr Term_redraw_section(TERM_LEN x1, TERM_LEN y1, TERM_LEN x2, TERM_LEN y2)
2228 {
2229         int i, j;
2230
2231         char *c_ptr;
2232
2233         /* Bounds checking */
2234         if (y2 >= Term->hgt) y2 = Term->hgt - 1;
2235         if (x2 >= Term->wid) x2 = Term->wid - 1;
2236         if (y1 < 0) y1 = 0;
2237         if (x1 < 0) x1 = 0;
2238
2239         /* Set y limits */
2240         Term->y1 = (byte_hack)y1;
2241         Term->y2 = (byte_hack)y2;
2242
2243         /* Set the x limits */
2244         for (i = Term->y1; i <= Term->y2; i++)
2245         {
2246 #ifdef JP
2247                 int x1j = x1;
2248                 int x2j = x2;
2249    
2250                 if (x1j > 0)
2251                 {
2252                         if (Term->scr->a[i][x1j] & AF_KANJI2) x1j--;
2253                 }
2254    
2255                 if (x2j < Term->wid - 1)
2256                 {
2257                         if (Term->scr->a[i][x2j] & AF_KANJI1) x2j++;
2258                 }
2259    
2260                 Term->x1[i] = (byte_hack)x1j;
2261                 Term->x2[i] = (byte_hack)x2j;
2262    
2263                 c_ptr = Term->old->c[i];
2264    
2265                 /* Clear the section so it is redrawn */
2266                 for (j = x1j; j <= x2j; j++)
2267                 {
2268                         /* Hack - set the old character to "none" */
2269                         c_ptr[j] = 0;
2270                 }
2271 #else
2272                 Term->x1[i] = x1;
2273                 Term->x2[i] = x2;
2274
2275                 c_ptr = Term->old->c[i];
2276
2277                 /* Clear the section so it is redrawn */
2278                 for (j = x1; j <= x2; j++)
2279                 {
2280                         /* Hack - set the old character to "none" */
2281                         c_ptr[j] = 0;
2282                 }
2283 #endif
2284         }
2285
2286         /* Hack -- Refresh */
2287         Term_fresh();
2288
2289         /* Success */
2290         return (0);
2291 }
2292
2293
2294
2295 /*** Access routines ***/
2296
2297
2298 /*
2299  * Extract the cursor visibility
2300  */
2301 errr Term_get_cursor(int *v)
2302 {
2303         /* Extract visibility */
2304         (*v) = Term->scr->cv;
2305
2306         /* Success */
2307         return (0);
2308 }
2309
2310
2311 /*
2312  * Extract the current window size
2313  */
2314 errr Term_get_size(TERM_LEN *w, TERM_LEN *h)
2315 {
2316         /* Access the cursor */
2317         (*w) = Term->wid;
2318         (*h) = Term->hgt;
2319
2320         /* Success */
2321         return (0);
2322 }
2323
2324
2325 /*
2326  * Extract the current cursor location
2327  */
2328 errr Term_locate(TERM_LEN *x, TERM_LEN *y)
2329 {
2330         /* Access the cursor */
2331         (*x) = Term->scr->cx;
2332         (*y) = Term->scr->cy;
2333
2334         /* Warn about "useless" cursor */
2335         if (Term->scr->cu) return (1);
2336
2337         /* Success */
2338         return (0);
2339 }
2340
2341
2342 /*
2343  * At a given location, determine the "current" attr and char
2344  * Note that this refers to what will be on the window after the
2345  * next call to "Term_fresh()".  It may or may not already be there.
2346  */
2347 errr Term_what(TERM_LEN x, TERM_LEN y, TERM_COLOR *a, char *c)
2348 {
2349         TERM_LEN w = Term->wid;
2350         TERM_LEN h = Term->hgt;
2351
2352         /* Verify location */
2353         if ((x < 0) || (x >= w)) return (-1);
2354         if ((y < 0) || (y >= h)) return (-1);
2355
2356         /* Direct access */
2357         (*a) = Term->scr->a[y][x];
2358         (*c) = Term->scr->c[y][x];
2359
2360         /* Success */
2361         return (0);
2362 }
2363
2364
2365
2366 /*** Input routines ***/
2367
2368
2369 /*
2370  * Flush and forget the input
2371  */
2372 errr Term_flush(void)
2373 {
2374         /* Hack -- Flush all events */
2375         Term_xtra(TERM_XTRA_FLUSH, 0);
2376
2377         /* Forget all keypresses */
2378         Term->key_head = Term->key_tail = 0;
2379
2380         /* Success */
2381         return (0);
2382 }
2383
2384
2385
2386 /*
2387  * Add a keypress to the "queue"
2388  */
2389 errr Term_keypress(int k)
2390 {
2391         /* Hack -- Refuse to enqueue non-keys */
2392         if (!k) return (-1);
2393
2394         /* Store the char, advance the queue */
2395         Term->key_queue[Term->key_head++] = (char)k;
2396
2397         /* Circular queue, handle wrap */
2398         if (Term->key_head == Term->key_size) Term->key_head = 0;
2399
2400         /* Success (unless overflow) */
2401         if (Term->key_head != Term->key_tail) return (0);
2402
2403 #if 0
2404         /* Hack -- Forget the oldest key */
2405         if (++Term->key_tail == Term->key_size) Term->key_tail = 0;
2406 #endif
2407
2408         /* Problem */
2409         return (1);
2410 }
2411
2412
2413 /*
2414  * Add a keypress to the FRONT of the "queue"
2415  */
2416 errr Term_key_push(int k)
2417 {
2418         /* Hack -- Refuse to enqueue non-keys */
2419         if (!k) return (-1);
2420
2421         /* Hack -- Overflow may induce circular queue */
2422         if (Term->key_tail == 0) Term->key_tail = Term->key_size;
2423
2424         /* Back up, Store the char */
2425         Term->key_queue[--Term->key_tail] = (char)k;
2426
2427         /* Success (unless overflow) */
2428         if (Term->key_head != Term->key_tail) return (0);
2429
2430         /* Problem */
2431         return (1);
2432 }
2433
2434
2435
2436
2437
2438 /*
2439  * Check for a pending keypress on the key queue.
2440  *
2441  * Store the keypress, if any, in "ch", and return "0".
2442  * Otherwise store "zero" in "ch", and return "1".
2443  *
2444  * Wait for a keypress if "wait" is true.
2445  *
2446  * Remove the keypress if "take" is true.
2447  */
2448 errr Term_inkey(char *ch, bool wait, bool take)
2449 {
2450         /* Assume no key */
2451         (*ch) = '\0';
2452
2453 #ifdef CHUUKEI
2454         flush_ringbuf();
2455 #endif
2456
2457         /* Hack -- get bored */
2458         if (!Term->never_bored)
2459         {
2460                 /* Process random events */
2461                 Term_xtra(TERM_XTRA_BORED, 0);
2462         }
2463
2464         /* Wait */
2465         if (wait)
2466         {
2467                 /* Process pending events while necessary */
2468                 while (Term->key_head == Term->key_tail)
2469                 {
2470                         /* Process events (wait for one) */
2471                         Term_xtra(TERM_XTRA_EVENT, TRUE);
2472                 }
2473         }
2474
2475         /* Do not Wait */
2476         else
2477         {
2478                 /* Process pending events if necessary */
2479                 if (Term->key_head == Term->key_tail)
2480                 {
2481                         /* Process events (do not wait) */
2482                         Term_xtra(TERM_XTRA_EVENT, FALSE);
2483                 }
2484         }
2485
2486         /* No keys are ready */
2487         if (Term->key_head == Term->key_tail) return (1);
2488
2489         /* Extract the next keypress */
2490         (*ch) = Term->key_queue[Term->key_tail];
2491
2492         /* If requested, advance the queue, wrap around if necessary */
2493         if (take && (++Term->key_tail == Term->key_size)) Term->key_tail = 0;
2494
2495         /* Success */
2496         return (0);
2497 }
2498
2499
2500
2501 /*** Extra routines ***/
2502
2503
2504 /*
2505  * Save the "requested" screen into the "memorized" screen
2506  *
2507  * Every "Term_save()" should match exactly one "Term_load()"
2508  */
2509 errr Term_save(void)
2510 {
2511         TERM_LEN w = Term->wid;
2512         TERM_LEN h = Term->hgt;
2513
2514         /* Create */
2515         if (!Term->mem)
2516         {
2517                 /* Allocate window */
2518                 MAKE(Term->mem, term_win);
2519
2520                 /* Initialize window */
2521                 term_win_init(Term->mem, w, h);
2522         }
2523
2524         /* Grab */
2525         term_win_copy(Term->mem, Term->scr, w, h);
2526
2527         /* Success */
2528         return (0);
2529 }
2530
2531
2532 /*
2533  * Restore the "requested" contents (see above).
2534  *
2535  * Every "Term_save()" should match exactly one "Term_load()"
2536  */
2537 errr Term_load(void)
2538 {
2539         TERM_LEN y;
2540
2541         TERM_LEN w = Term->wid;
2542         TERM_LEN h = Term->hgt;
2543
2544         /* Create */
2545         if (!Term->mem)
2546         {
2547                 /* Allocate window */
2548                 MAKE(Term->mem, term_win);
2549
2550                 /* Initialize window */
2551                 term_win_init(Term->mem, w, h);
2552         }
2553
2554         /* Load */
2555         term_win_copy(Term->scr, Term->mem, w, h);
2556
2557         /* Assume change */
2558         for (y = 0; y < h; y++)
2559         {
2560                 /* Assume change */
2561                 Term->x1[y] = 0;
2562                 Term->x2[y] = w - 1;
2563         }
2564
2565         /* Assume change */
2566         Term->y1 = 0;
2567         Term->y2 = h - 1;
2568
2569         /* Success */
2570         return (0);
2571 }
2572
2573
2574 /*
2575  * Exchange the "requested" screen with the "tmp" screen
2576  */
2577 errr Term_exchange(void)
2578 {
2579         TERM_LEN y;
2580
2581         TERM_LEN w = Term->wid;
2582         TERM_LEN h = Term->hgt;
2583
2584         term_win *exchanger;
2585
2586
2587         /* Create */
2588         if (!Term->tmp)
2589         {
2590                 /* Allocate window */
2591                 MAKE(Term->tmp, term_win);
2592
2593                 /* Initialize window */
2594                 term_win_init(Term->tmp, w, h);
2595         }
2596
2597         /* Swap */
2598         exchanger = Term->scr;
2599         Term->scr = Term->tmp;
2600         Term->tmp = exchanger;
2601
2602         /* Assume change */
2603         for (y = 0; y < h; y++)
2604         {
2605                 /* Assume change */
2606                 Term->x1[y] = 0;
2607                 Term->x2[y] = w - 1;
2608         }
2609
2610         /* Assume change */
2611         Term->y1 = 0;
2612         Term->y2 = h - 1;
2613
2614         /* Success */
2615         return (0);
2616 }
2617
2618
2619
2620 /*
2621  * React to a new physical window size.
2622  */
2623 errr Term_resize(TERM_LEN w, TERM_LEN h)
2624 {
2625         int i;
2626
2627         TERM_LEN wid, hgt;
2628
2629         TERM_LEN *hold_x1;
2630         TERM_LEN *hold_x2;
2631
2632         term_win *hold_old;
2633         term_win *hold_scr;
2634         term_win *hold_mem;
2635         term_win *hold_tmp;
2636
2637         /* Resizing is forbidden */
2638         if (Term->fixed_shape) return (-1);
2639
2640         /* Ignore illegal changes */
2641         if ((w < 1) || (h < 1)) return (-1);
2642
2643
2644         /* Ignore non-changes */
2645         if ((Term->wid == w) && (Term->hgt == h) && (arg_bigtile == use_bigtile))
2646                 return (1);
2647
2648         use_bigtile = arg_bigtile;
2649
2650         /* Minimum dimensions */
2651         wid = MIN(Term->wid, w);
2652         hgt = MIN(Term->hgt, h);
2653
2654         /* Save scanners */
2655         hold_x1 = Term->x1;
2656         hold_x2 = Term->x2;
2657
2658         /* Save old window */
2659         hold_old = Term->old;
2660
2661         /* Save old window */
2662         hold_scr = Term->scr;
2663
2664         /* Save old window */
2665         hold_mem = Term->mem;
2666
2667         /* Save old window */
2668         hold_tmp = Term->tmp;
2669
2670         /* Create new scanners */
2671         C_MAKE(Term->x1, h, TERM_LEN);
2672         C_MAKE(Term->x2, h, TERM_LEN);
2673
2674         /* Create new window */
2675         MAKE(Term->old, term_win);
2676
2677         /* Initialize new window */
2678         term_win_init(Term->old, w, h);
2679
2680         /* Save the contents */
2681         term_win_copy(Term->old, hold_old, wid, hgt);
2682
2683         /* Create new window */
2684         MAKE(Term->scr, term_win);
2685
2686         /* Initialize new window */
2687         term_win_init(Term->scr, w, h);
2688
2689         /* Save the contents */
2690         term_win_copy(Term->scr, hold_scr, wid, hgt);
2691
2692         /* If needed */
2693         if (hold_mem)
2694         {
2695                 /* Create new window */
2696                 MAKE(Term->mem, term_win);
2697
2698                 /* Initialize new window */
2699                 term_win_init(Term->mem, w, h);
2700
2701                 /* Save the contents */
2702                 term_win_copy(Term->mem, hold_mem, wid, hgt);
2703         }
2704
2705         /* If needed */
2706         if (hold_tmp)
2707         {
2708                 /* Create new window */
2709                 MAKE(Term->tmp, term_win);
2710
2711                 /* Initialize new window */
2712                 term_win_init(Term->tmp, w, h);
2713
2714                 /* Save the contents */
2715                 term_win_copy(Term->tmp, hold_tmp, wid, hgt);
2716         }
2717
2718         /* Free some arrays */
2719         C_KILL(hold_x1, Term->hgt, TERM_LEN);
2720         C_KILL(hold_x2, Term->hgt, TERM_LEN);
2721
2722         /* Nuke */
2723         term_win_nuke(hold_old, Term->wid, Term->hgt);
2724
2725         /* Kill */
2726         KILL(hold_old, term_win);
2727
2728         /* Illegal cursor */
2729         if (Term->old->cx >= w) Term->old->cu = 1;
2730         if (Term->old->cy >= h) Term->old->cu = 1;
2731
2732         /* Nuke */
2733         term_win_nuke(hold_scr, Term->wid, Term->hgt);
2734
2735         /* Kill */
2736         KILL(hold_scr, term_win);
2737
2738         /* Illegal cursor */
2739         if (Term->scr->cx >= w) Term->scr->cu = 1;
2740         if (Term->scr->cy >= h) Term->scr->cu = 1;
2741
2742         /* If needed */
2743         if (hold_mem)
2744         {
2745                 /* Nuke */
2746                 term_win_nuke(hold_mem, Term->wid, Term->hgt);
2747
2748                 /* Kill */
2749                 KILL(hold_mem, term_win);
2750
2751                 /* Illegal cursor */
2752                 if (Term->mem->cx >= w) Term->mem->cu = 1;
2753                 if (Term->mem->cy >= h) Term->mem->cu = 1;
2754         }
2755
2756         /* If needed */
2757         if (hold_tmp)
2758         {
2759                 /* Nuke */
2760                 term_win_nuke(hold_tmp, Term->wid, Term->hgt);
2761
2762                 /* Kill */
2763                 KILL(hold_tmp, term_win);
2764
2765                 /* Illegal cursor */
2766                 if (Term->tmp->cx >= w) Term->tmp->cu = 1;
2767                 if (Term->tmp->cy >= h) Term->tmp->cu = 1;
2768         }
2769
2770         /* Save new size */
2771         Term->wid = w;
2772         Term->hgt = h;
2773
2774         /* Force "total erase" */
2775         Term->total_erase = TRUE;
2776
2777         /* Assume change */
2778         for (i = 0; i < h; i++)
2779         {
2780                 /* Assume change */
2781                 Term->x1[i] = 0;
2782                 Term->x2[i] = w - 1;
2783         }
2784
2785         /* Assume change */
2786         Term->y1 = 0;
2787         Term->y2 = h - 1;
2788
2789         /* Execute the "resize_hook" hook, if available */
2790         if (Term->resize_hook)
2791         {
2792                 Term->resize_hook();
2793         }
2794
2795         /* Success */
2796         return (0);
2797 }
2798
2799
2800
2801 /*
2802  * Activate a new Term (and deactivate the current Term)
2803  *
2804  * This function is extremely important, and also somewhat bizarre.
2805  * It is the only function that should "modify" the value of "Term".
2806  *
2807  * To "create" a valid "term", one should do "term_init(t)", then
2808  * set the various flags and hooks, and then do "Term_activate(t)".
2809  */
2810 errr Term_activate(term *t)
2811 {
2812         /* Hack -- already done */
2813         if (Term == t) return (1);
2814
2815         /* Deactivate the old Term */
2816         if (Term) Term_xtra(TERM_XTRA_LEVEL, 0);
2817
2818         /* Hack -- Call the special "init" hook */
2819         if (t && !t->active_flag)
2820         {
2821                 /* Call the "init" hook */
2822                 if (t->init_hook) (*t->init_hook)(t);
2823
2824                 /* Remember */
2825                 t->active_flag = TRUE;
2826
2827                 /* Assume mapped */
2828                 t->mapped_flag = TRUE;
2829         }
2830
2831         /* Remember the Term */
2832         Term = t;
2833
2834         /* Activate the new Term */
2835         if (Term) Term_xtra(TERM_XTRA_LEVEL, 1);
2836
2837         /* Success */
2838         return (0);
2839 }
2840
2841
2842
2843 /*
2844  * Nuke a term
2845  */
2846 errr term_nuke(term *t)
2847 {
2848         TERM_LEN w = t->wid;
2849         TERM_LEN h = t->hgt;
2850
2851
2852         /* Hack -- Call the special "nuke" hook */
2853         if (t->active_flag)
2854         {
2855                 /* Call the "nuke" hook */
2856                 if (t->nuke_hook) (*t->nuke_hook)(t);
2857
2858                 /* Remember */
2859                 t->active_flag = FALSE;
2860
2861                 /* Assume not mapped */
2862                 t->mapped_flag = FALSE;
2863         }
2864
2865
2866         /* Nuke "displayed" */
2867         term_win_nuke(t->old, w, h);
2868
2869         /* Kill "displayed" */
2870         KILL(t->old, term_win);
2871
2872         /* Nuke "requested" */
2873         term_win_nuke(t->scr, w, h);
2874
2875         /* Kill "requested" */
2876         KILL(t->scr, term_win);
2877
2878         /* If needed */
2879         if (t->mem)
2880         {
2881                 /* Nuke "memorized" */
2882                 term_win_nuke(t->mem, w, h);
2883
2884                 /* Kill "memorized" */
2885                 KILL(t->mem, term_win);
2886         }
2887
2888         /* If needed */
2889         if (t->tmp)
2890         {
2891                 /* Nuke "temporary" */
2892                 term_win_nuke(t->tmp, w, h);
2893
2894                 /* Kill "temporary" */
2895                 KILL(t->tmp, term_win);
2896         }
2897
2898         /* Free some arrays */
2899         C_KILL(t->x1, h, TERM_LEN);
2900         C_KILL(t->x2, h, TERM_LEN);
2901
2902         /* Free the input queue */
2903         C_KILL(t->key_queue, t->key_size, char);
2904
2905         /* Success */
2906         return (0);
2907 }
2908
2909
2910 /*
2911  * Initialize a term, using a window of the given size.
2912  * Also prepare the "input queue" for "k" keypresses
2913  * By default, the cursor starts out "invisible"
2914  * By default, we "erase" using "black spaces"
2915  */
2916 errr term_init(term *t, TERM_LEN w, TERM_LEN h, int k)
2917 {
2918         TERM_LEN y;
2919
2920
2921         /* Wipe it */
2922         (void)WIPE(t, term);
2923
2924
2925         /* Prepare the input queue */
2926         t->key_head = t->key_tail = 0;
2927
2928         /* Determine the input queue size */
2929         t->key_size = (u16b)k;
2930
2931         /* Allocate the input queue */
2932         C_MAKE(t->key_queue, t->key_size, char);
2933
2934
2935         /* Save the size */
2936         t->wid = w;
2937         t->hgt = h;
2938
2939         /* Allocate change arrays */
2940         C_MAKE(t->x1, h, TERM_LEN);
2941         C_MAKE(t->x2, h, TERM_LEN);
2942
2943
2944         /* Allocate "displayed" */
2945         MAKE(t->old, term_win);
2946
2947         /* Initialize "displayed" */
2948         term_win_init(t->old, w, h);
2949
2950
2951         /* Allocate "requested" */
2952         MAKE(t->scr, term_win);
2953
2954         /* Initialize "requested" */
2955         term_win_init(t->scr, w, h);
2956
2957
2958         /* Assume change */
2959         for (y = 0; y < h; y++)
2960         {
2961                 /* Assume change */
2962                 t->x1[y] = 0;
2963                 t->x2[y] = w - 1;
2964         }
2965
2966         /* Assume change */
2967         t->y1 = 0;
2968         t->y2 = h - 1;
2969
2970         /* Force "total erase" */
2971         t->total_erase = TRUE;
2972
2973
2974         /* Default "blank" */
2975         t->attr_blank = 0;
2976         t->char_blank = ' ';
2977
2978
2979         /* Prepare "fake" hooks to prevent core dumps */
2980         t->curs_hook = Term_curs_hack;
2981         t->bigcurs_hook = Term_bigcurs_hack;
2982         t->wipe_hook = Term_wipe_hack;
2983         t->text_hook = Term_text_hack;
2984         t->pict_hook = Term_pict_hack;
2985
2986
2987         /* Success */
2988         return (0);
2989 }
2990
2991