OSDN Git Service

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