OSDN Git Service

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