OSDN Git Service

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