OSDN Git Service

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